react-grid-layout 1.5.3 → 2.1.0
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/README.md +1156 -442
- package/css/styles.css +5 -4
- package/dist/ResponsiveGridLayout-BkHF9YHa.d.mts +133 -0
- package/dist/ResponsiveGridLayout-CLn16-X3.d.ts +133 -0
- package/dist/calculate-DsVTldEE.d.ts +196 -0
- package/dist/calculate-DwbL1D06.d.mts +196 -0
- package/dist/chunk-2O3ZME4Q.mjs +703 -0
- package/dist/chunk-2O3ZME4Q.mjs.map +1 -0
- package/dist/chunk-526JND3R.mjs +3 -0
- package/dist/chunk-526JND3R.mjs.map +1 -0
- package/dist/chunk-7ELO5FRW.js +4 -0
- package/dist/chunk-7ELO5FRW.js.map +1 -0
- package/dist/chunk-AWM66AWF.mjs +422 -0
- package/dist/chunk-AWM66AWF.mjs.map +1 -0
- package/dist/chunk-BJFPTW5Q.js +449 -0
- package/dist/chunk-BJFPTW5Q.js.map +1 -0
- package/dist/chunk-FN6MIZ24.js +744 -0
- package/dist/chunk-FN6MIZ24.js.map +1 -0
- package/dist/chunk-GPZBANOK.js +435 -0
- package/dist/chunk-GPZBANOK.js.map +1 -0
- package/dist/chunk-LWF5EYMO.mjs +429 -0
- package/dist/chunk-LWF5EYMO.mjs.map +1 -0
- package/dist/chunk-MXXN7GUE.mjs +1408 -0
- package/dist/chunk-MXXN7GUE.mjs.map +1 -0
- package/dist/chunk-QRW3ND4U.js +1417 -0
- package/dist/chunk-QRW3ND4U.js.map +1 -0
- package/dist/core.d.mts +186 -0
- package/dist/core.d.ts +186 -0
- package/dist/core.js +274 -0
- package/dist/core.js.map +1 -0
- package/dist/core.mjs +5 -0
- package/dist/core.mjs.map +1 -0
- package/dist/extras.d.mts +208 -0
- package/dist/extras.d.ts +208 -0
- package/dist/extras.js +462 -0
- package/dist/extras.js.map +1 -0
- package/dist/extras.mjs +454 -0
- package/dist/extras.mjs.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/legacy.d.mts +163 -0
- package/dist/legacy.d.ts +163 -0
- package/dist/legacy.js +320 -0
- package/dist/legacy.js.map +1 -0
- package/dist/legacy.mjs +308 -0
- package/dist/legacy.mjs.map +1 -0
- package/dist/position-C8a5g6bb.d.mts +324 -0
- package/dist/position-H8tBL91b.d.ts +324 -0
- package/dist/react.d.mts +357 -0
- package/dist/react.d.ts +357 -0
- package/dist/react.js +96 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +7 -0
- package/dist/react.mjs.map +1 -0
- package/dist/responsive-BB5d98xz.d.mts +145 -0
- package/dist/responsive-BXI-GCXG.d.ts +145 -0
- package/dist/types-CokovIMH.d.mts +469 -0
- package/dist/types-CokovIMH.d.ts +469 -0
- package/index-dev.js +24 -0
- package/package.json +133 -40
- package/.babelrc.js +0 -22
- package/.browserslistrc +0 -3
- package/.eslintignore +0 -4
- package/.flowconfig +0 -20
- package/.prettierignore +0 -4
- package/.prettierrc +0 -17
- package/CHANGELOG.md +0 -985
- package/build/GridItem.js +0 -638
- package/build/ReactGridLayout.js +0 -741
- package/build/ReactGridLayoutPropTypes.js +0 -210
- package/build/ResponsiveReactGridLayout.js +0 -297
- package/build/calculateUtils.js +0 -165
- package/build/components/WidthProvider.js +0 -115
- package/build/fastRGLPropsEqual.js +0 -5
- package/build/responsiveUtils.js +0 -101
- package/build/utils.js +0 -842
- package/dist/react-grid-layout.min.js +0 -2
- package/dist/react-grid-layout.min.js.map +0 -1
- package/index.js.flow +0 -8
- package/ip_fetcher +0 -0
- package/ip_fetcher.c +0 -47
- package/lib/GridItem.jsx +0 -689
- package/lib/ReactGridLayout.jsx +0 -855
- package/lib/ReactGridLayoutPropTypes.js +0 -241
- package/lib/ResponsiveReactGridLayout.jsx +0 -349
- package/lib/calculateUtils.js +0 -169
- package/lib/components/WidthProvider.jsx +0 -110
- package/lib/fastRGLPropsEqual.js +0 -46
- package/lib/responsiveUtils.js +0 -118
- package/lib/utils.js +0 -995
- package/yarn-error.log +0 -7780
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# React-Grid-Layout
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/STRML/react-grid-layout)
|
|
4
|
-
[](https://cdnjs.com/libraries/react-grid-layout)
|
|
5
3
|
[](https://www.npmjs.org/package/react-grid-layout)
|
|
6
4
|
[]()
|
|
7
5
|
|
|
@@ -21,19 +19,97 @@ RGL is React-only and does not require jQuery.
|
|
|
21
19
|
|
|
22
20
|
## Table of Contents
|
|
23
21
|
|
|
22
|
+
- [What's New in v2](#whats-new-in-v2)
|
|
23
|
+
- [Migrating from v1](#migrating-from-v1)
|
|
24
24
|
- [Demos](#demos)
|
|
25
25
|
- [Features](#features)
|
|
26
26
|
- [Installation](#installation)
|
|
27
|
-
- [
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
28
|
- [Responsive Usage](#responsive-usage)
|
|
29
29
|
- [Providing Grid Width](#providing-grid-width)
|
|
30
|
-
- [
|
|
31
|
-
- [
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
30
|
+
- [Hooks API](#hooks-api)
|
|
31
|
+
- [API Reference](#api-reference)
|
|
32
|
+
- [Extending: Custom Compactors & Position Strategies](#extending-custom-compactors--position-strategies)
|
|
33
|
+
- [Extras](#extras)
|
|
34
34
|
- [Performance](#performance)
|
|
35
35
|
- [Contribute](#contribute)
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
## What's New in v2
|
|
38
|
+
|
|
39
|
+
Version 2 is a complete TypeScript rewrite with a modernized API:
|
|
40
|
+
|
|
41
|
+
- **Full TypeScript support** - First-class types, no more `@types/react-grid-layout`
|
|
42
|
+
- **React Hooks** - New `useContainerWidth`, `useGridLayout`, and `useResponsiveLayout` hooks
|
|
43
|
+
- **Composable Configuration** - Group related props into focused interfaces:
|
|
44
|
+
- `gridConfig` - cols, rowHeight, margin, padding
|
|
45
|
+
- `dragConfig` - enable, handle, cancel, bounded
|
|
46
|
+
- `resizeConfig` - enable, handles
|
|
47
|
+
- `positionStrategy` - transform vs absolute positioning
|
|
48
|
+
- `compactor` - vertical, horizontal, or custom algorithms
|
|
49
|
+
- **Modular architecture** - Import only what you need:
|
|
50
|
+
- `react-grid-layout` - React components and hooks (v2 API)
|
|
51
|
+
- `react-grid-layout/core` - Pure layout algorithms (framework-agnostic)
|
|
52
|
+
- `react-grid-layout/legacy` - v1 flat props API for migration
|
|
53
|
+
- `react-grid-layout/extras` - Optional components like `GridBackground`
|
|
54
|
+
- **Smaller bundle** - Tree-shakeable ESM and CJS builds
|
|
55
|
+
|
|
56
|
+
### Breaking Changes
|
|
57
|
+
|
|
58
|
+
See the [RFC](./rfcs/0001-v2-typescript-rewrite.md#breaking-changes-in-v2) for detailed migration examples.
|
|
59
|
+
|
|
60
|
+
| Change | Description |
|
|
61
|
+
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
62
|
+
| [`width` prop required](./rfcs/0001-v2-typescript-rewrite.md#breaking-changes-in-v2) | Use `useContainerWidth` hook or provide your own measurement |
|
|
63
|
+
| [`onDragStart` threshold](./rfcs/0001-v2-typescript-rewrite.md#1-ondragstart-no-longer-fires-on-click-only-events) | Now fires after 3px movement, not on mousedown. Use `onMouseDown` for immediate response |
|
|
64
|
+
| [Immutable callbacks](./rfcs/0001-v2-typescript-rewrite.md#2-immutable-callback-parameters) | Callback parameters are read-only. Use `onLayoutChange` or constraints instead of mutation |
|
|
65
|
+
| [`data-grid` in legacy only](./rfcs/0001-v2-typescript-rewrite.md#3-data-grid-prop-only-available-in-legacy-wrapper) | v2 requires explicit `layout` prop. Use legacy wrapper for `data-grid` |
|
|
66
|
+
| [Pluggable compaction](./rfcs/0001-v2-typescript-rewrite.md#4-pluggable-compaction-algorithms) | Compaction is now pluggable via `Compactor` interface. Optional fast O(n log n) algorithm in `/extras` |
|
|
67
|
+
| UMD bundle removed | Use a bundler (Vite, webpack, esbuild) |
|
|
68
|
+
| `verticalCompact` removed | Use `compactType={null}` or `compactor={noCompactor}` |
|
|
69
|
+
|
|
70
|
+
## Migrating from v1
|
|
71
|
+
|
|
72
|
+
**Quick migration** - change your import to use the legacy wrapper:
|
|
73
|
+
|
|
74
|
+
```diff
|
|
75
|
+
- import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout';
|
|
76
|
+
+ import GridLayout, { Responsive, WidthProvider } from 'react-grid-layout/legacy';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This provides **100% API compatibility** with v1.
|
|
80
|
+
|
|
81
|
+
**Full migration** - adopt the v2 API for new features and better tree-shaking:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import ReactGridLayout, { useContainerWidth, verticalCompactor } from 'react-grid-layout';
|
|
85
|
+
|
|
86
|
+
function MyGrid() {
|
|
87
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div ref={containerRef}>
|
|
91
|
+
{mounted && (
|
|
92
|
+
<ReactGridLayout
|
|
93
|
+
width={width}
|
|
94
|
+
layout={layout}
|
|
95
|
+
gridConfig={{ cols: 12, rowHeight: 30 }}
|
|
96
|
+
dragConfig={{ enabled: true, handle: '.handle' }}
|
|
97
|
+
compactor={verticalCompactor}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</ReactGridLayout>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Use Case | Recommendation |
|
|
108
|
+
| -------------------- | ---------------------------------- |
|
|
109
|
+
| Existing v1 codebase | `react-grid-layout/legacy` |
|
|
110
|
+
| New project | v2 API with hooks |
|
|
111
|
+
| Custom compaction | v2 with custom `Compactor` |
|
|
112
|
+
| SSR | v2 with `measureBeforeMount: true` |
|
|
37
113
|
|
|
38
114
|
## Demos
|
|
39
115
|
|
|
@@ -48,17 +124,17 @@ RGL is React-only and does not require jQuery.
|
|
|
48
124
|
1. [Saving a Responsive Layout to LocalStorage](https://react-grid-layout.github.io/react-grid-layout/examples/08-localstorage-responsive.html)
|
|
49
125
|
1. [Minimum and Maximum Width/Height](https://react-grid-layout.github.io/react-grid-layout/examples/09-min-max-wh.html)
|
|
50
126
|
1. [Dynamic Minimum and Maximum Width/Height](https://react-grid-layout.github.io/react-grid-layout/examples/10-dynamic-min-max-wh.html)
|
|
51
|
-
1. [
|
|
52
|
-
1. [
|
|
53
|
-
1. [
|
|
54
|
-
1. [
|
|
55
|
-
1. [
|
|
56
|
-
1. [
|
|
57
|
-
1. [
|
|
58
|
-
1. [
|
|
59
|
-
1. [
|
|
60
|
-
1. [
|
|
61
|
-
1. [
|
|
127
|
+
1. [Toolbox](https://react-grid-layout.github.io/react-grid-layout/examples/11-toolbox.html)
|
|
128
|
+
1. [Drag From Outside](https://react-grid-layout.github.io/react-grid-layout/examples/12-drag-from-outside.html)
|
|
129
|
+
1. [Bounded Layout](https://react-grid-layout.github.io/react-grid-layout/examples/13-bounded.html)
|
|
130
|
+
1. [Responsive Bootstrap-style Layout](https://react-grid-layout.github.io/react-grid-layout/examples/14-responsive-bootstrap-style.html)
|
|
131
|
+
1. [Scaled Containers](https://react-grid-layout.github.io/react-grid-layout/examples/15-scale.html)
|
|
132
|
+
1. [Allow Overlap](https://react-grid-layout.github.io/react-grid-layout/examples/16-allow-overlap.html)
|
|
133
|
+
1. [All Resizable Handles](https://react-grid-layout.github.io/react-grid-layout/examples/17-resizable-handles.html)
|
|
134
|
+
1. [Compactor Showcase](https://react-grid-layout.github.io/react-grid-layout/examples/18-compactors.html)
|
|
135
|
+
1. [Pluggable Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/19-constraints.html)
|
|
136
|
+
1. [Aspect Ratio Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/20-aspect-ratio.html)
|
|
137
|
+
1. [Custom Constraints](https://react-grid-layout.github.io/react-grid-layout/examples/21-custom-constraints.html)
|
|
62
138
|
|
|
63
139
|
#### Projects Using React-Grid-Layout
|
|
64
140
|
|
|
@@ -68,25 +144,15 @@ RGL is React-only and does not require jQuery.
|
|
|
68
144
|
- [Grafana](https://grafana.com/)
|
|
69
145
|
- [Metabase](http://www.metabase.com/)
|
|
70
146
|
- [HubSpot](http://www.hubspot.com)
|
|
71
|
-
- [ComNetViz](http://www.grotto-networking.com/ComNetViz/ComNetViz.html)
|
|
72
|
-
- [Stoplight](https://app.stoplight.io)
|
|
73
|
-
- [Reflect](https://reflect.io)
|
|
74
|
-
- [ez-Dashing](https://github.com/ylacaute/ez-Dashing)
|
|
75
147
|
- [Kibana](https://www.elastic.co/products/kibana)
|
|
76
|
-
- [Graphext](https://graphext.com/)
|
|
77
148
|
- [Monday](https://support.monday.com/hc/en-us/articles/360002187819-What-are-the-Dashboards-)
|
|
78
|
-
- [Quadency](https://quadency.com/)
|
|
79
|
-
- [Hakkiri](https://www.hakkiri.io)
|
|
80
|
-
- [Ubidots](https://help.ubidots.com/en/articles/2400308-create-dashboards-and-widgets)
|
|
81
|
-
- [Statsout](https://statsout.com/)
|
|
82
|
-
- [Datto RMM](https://www.datto.com/uk/products/rmm/)
|
|
83
|
-
- [SquaredUp](https://squaredup.com/)
|
|
84
149
|
|
|
85
150
|
_Know of others? Create a PR to let me know!_
|
|
86
151
|
|
|
87
152
|
## Features
|
|
88
153
|
|
|
89
154
|
- 100% React - no jQuery
|
|
155
|
+
- Full TypeScript support
|
|
90
156
|
- Compatible with server-rendered apps
|
|
91
157
|
- Draggable widgets
|
|
92
158
|
- Resizable widgets
|
|
@@ -98,506 +164,1170 @@ _Know of others? Create a PR to let me know!_
|
|
|
98
164
|
- Responsive breakpoints
|
|
99
165
|
- Separate layouts per responsive breakpoint
|
|
100
166
|
- Grid Items placed using CSS Transforms
|
|
101
|
-
- Performance with CSS Transforms: [on](http://i.imgur.com/FTogpLp.jpg) / [off](http://i.imgur.com/gOveMm8.jpg), note paint (green) as % of time
|
|
102
167
|
- Compatibility with `<React.StrictMode>`
|
|
103
168
|
|
|
104
|
-
| Version
|
|
105
|
-
|
|
|
106
|
-
| >= 0.
|
|
107
|
-
| >= 0.
|
|
108
|
-
| >= 0.10.0 | React 0.14 |
|
|
109
|
-
| 0.8. - 0.9.2 | React 0.13 |
|
|
110
|
-
| < 0.8 | React 0.12 |
|
|
169
|
+
| Version | Compatibility |
|
|
170
|
+
| --------- | --------------------- |
|
|
171
|
+
| >= 2.0.0 | React 18+, TypeScript |
|
|
172
|
+
| >= 0.17.0 | React 16 & 17 |
|
|
111
173
|
|
|
112
174
|
## Installation
|
|
113
175
|
|
|
114
|
-
Install the React-Grid-Layout [package](https://www.npmjs.org/package/react-grid-layout) using [npm](https://www.npmjs.com/):
|
|
115
|
-
|
|
116
176
|
```bash
|
|
117
177
|
npm install react-grid-layout
|
|
118
178
|
```
|
|
119
179
|
|
|
120
|
-
Include the
|
|
180
|
+
Include the stylesheets in your application:
|
|
121
181
|
|
|
182
|
+
```js
|
|
183
|
+
import "react-grid-layout/css/styles.css";
|
|
184
|
+
import "react-resizable/css/styles.css";
|
|
122
185
|
```
|
|
123
|
-
|
|
124
|
-
|
|
186
|
+
|
|
187
|
+
Or link them directly:
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<link rel="stylesheet" href="/node_modules/react-grid-layout/css/styles.css" />
|
|
191
|
+
<link rel="stylesheet" href="/node_modules/react-resizable/css/styles.css" />
|
|
125
192
|
```
|
|
126
193
|
|
|
127
|
-
##
|
|
194
|
+
## Quick Start
|
|
128
195
|
|
|
129
|
-
|
|
130
|
-
|
|
196
|
+
```tsx
|
|
197
|
+
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
|
|
198
|
+
import "react-grid-layout/css/styles.css";
|
|
199
|
+
import "react-resizable/css/styles.css";
|
|
131
200
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
- users will be able to freely drag and resize item `c`
|
|
201
|
+
function MyGrid() {
|
|
202
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
135
203
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<div key="c">c</div>
|
|
158
|
-
</GridLayout>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
204
|
+
const layout = [
|
|
205
|
+
{ i: "a", x: 0, y: 0, w: 1, h: 2, static: true },
|
|
206
|
+
{ i: "b", x: 1, y: 0, w: 3, h: 2, minW: 2, maxW: 4 },
|
|
207
|
+
{ i: "c", x: 4, y: 0, w: 1, h: 2 }
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<div ref={containerRef}>
|
|
212
|
+
{mounted && (
|
|
213
|
+
<ReactGridLayout
|
|
214
|
+
layout={layout}
|
|
215
|
+
width={width}
|
|
216
|
+
gridConfig={{ cols: 12, rowHeight: 30 }}
|
|
217
|
+
>
|
|
218
|
+
<div key="a">a</div>
|
|
219
|
+
<div key="b">b</div>
|
|
220
|
+
<div key="c">c</div>
|
|
221
|
+
</ReactGridLayout>
|
|
222
|
+
)}
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
161
225
|
}
|
|
162
226
|
```
|
|
163
227
|
|
|
164
|
-
You
|
|
228
|
+
You can also define layout on children using `data-grid`:
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
<ReactGridLayout width={width} gridConfig={{ cols: 12, rowHeight: 30 }}>
|
|
232
|
+
<div key="a" data-grid={{ x: 0, y: 0, w: 1, h: 2, static: true }}>
|
|
233
|
+
a
|
|
234
|
+
</div>
|
|
235
|
+
<div key="b" data-grid={{ x: 1, y: 0, w: 3, h: 2 }}>
|
|
236
|
+
b
|
|
237
|
+
</div>
|
|
238
|
+
<div key="c" data-grid={{ x: 4, y: 0, w: 1, h: 2 }}>
|
|
239
|
+
c
|
|
240
|
+
</div>
|
|
241
|
+
</ReactGridLayout>
|
|
242
|
+
```
|
|
165
243
|
|
|
166
|
-
|
|
167
|
-
import GridLayout from "react-grid-layout";
|
|
244
|
+
## Responsive Usage
|
|
168
245
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
246
|
+
Use `Responsive` for automatic breakpoint handling:
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { Responsive, useContainerWidth } from "react-grid-layout";
|
|
250
|
+
|
|
251
|
+
function MyResponsiveGrid() {
|
|
252
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
253
|
+
|
|
254
|
+
const layouts = {
|
|
255
|
+
lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
|
|
256
|
+
md: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }]
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<div ref={containerRef}>
|
|
261
|
+
{mounted && (
|
|
262
|
+
<Responsive
|
|
263
|
+
layouts={layouts}
|
|
264
|
+
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
265
|
+
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
266
|
+
width={width}
|
|
267
|
+
>
|
|
268
|
+
<div key="1">1</div>
|
|
269
|
+
<div key="2">2</div>
|
|
270
|
+
<div key="3">3</div>
|
|
271
|
+
</Responsive>
|
|
272
|
+
)}
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
185
275
|
}
|
|
186
276
|
```
|
|
187
277
|
|
|
188
|
-
|
|
278
|
+
## Providing Grid Width
|
|
189
279
|
|
|
190
|
-
|
|
191
|
-
excludes `React`, so it must be otherwise available in your application, either via RequireJS or on `window.React`.
|
|
280
|
+
The `width` prop is required. You have several options:
|
|
192
281
|
|
|
193
|
-
###
|
|
282
|
+
### Option 1: useContainerWidth Hook (Recommended)
|
|
194
283
|
|
|
195
|
-
|
|
284
|
+
```tsx
|
|
285
|
+
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
|
|
196
286
|
|
|
197
|
-
|
|
198
|
-
|
|
287
|
+
function MyGrid() {
|
|
288
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
199
289
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<ResponsiveGridLayout
|
|
206
|
-
className="layout"
|
|
207
|
-
layouts={layouts}
|
|
208
|
-
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
209
|
-
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
210
|
-
>
|
|
211
|
-
<div key="1">1</div>
|
|
212
|
-
<div key="2">2</div>
|
|
213
|
-
<div key="3">3</div>
|
|
214
|
-
</ResponsiveGridLayout>
|
|
215
|
-
);
|
|
216
|
-
}
|
|
290
|
+
return (
|
|
291
|
+
<div ref={containerRef}>
|
|
292
|
+
{mounted && <ReactGridLayout width={width}>...</ReactGridLayout>}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
217
295
|
}
|
|
218
296
|
```
|
|
219
297
|
|
|
220
|
-
|
|
298
|
+
### Option 2: Fixed Width
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<ReactGridLayout width={1200}>...</ReactGridLayout>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Option 3: CSS Container Queries or ResizeObserver
|
|
221
305
|
|
|
222
|
-
|
|
223
|
-
If the largest is provided, RGL will attempt to interpolate the rest.
|
|
306
|
+
Use any width measurement library like [react-sizeme](https://github.com/ctrlplusb/react-sizeme) or your own ResizeObserver implementation.
|
|
224
307
|
|
|
225
|
-
|
|
226
|
-
`WidthProvider` as per the instructions below.
|
|
308
|
+
### Option 4: Legacy WidthProvider HOC
|
|
227
309
|
|
|
228
|
-
|
|
229
|
-
items, so that they would be taken into account within layout interpolation.
|
|
310
|
+
For backwards compatibility, you can still use `WidthProvider`:
|
|
230
311
|
|
|
231
|
-
|
|
312
|
+
```tsx
|
|
313
|
+
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
|
|
232
314
|
|
|
233
|
-
|
|
234
|
-
positions on drag events. In simple cases a HOC `WidthProvider` can be used to automatically determine
|
|
235
|
-
width upon initialization and window resize events.
|
|
315
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
236
316
|
|
|
237
|
-
|
|
238
|
-
|
|
317
|
+
function MyGrid() {
|
|
318
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
319
|
+
}
|
|
320
|
+
```
|
|
239
321
|
|
|
240
|
-
|
|
322
|
+
## Hooks API
|
|
241
323
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
324
|
+
The v2 API provides three hooks for different use cases. Choose based on your needs:
|
|
325
|
+
|
|
326
|
+
| Hook | Use When |
|
|
327
|
+
| --------------------- | -------------------------------------------------------------------- |
|
|
328
|
+
| `useContainerWidth` | You need responsive width measurement (most common) |
|
|
329
|
+
| `useGridLayout` | You're building a custom grid component or need direct state control |
|
|
330
|
+
| `useResponsiveLayout` | You're building a custom responsive grid with breakpoint logic |
|
|
331
|
+
|
|
332
|
+
### useContainerWidth
|
|
333
|
+
|
|
334
|
+
Observes container width using ResizeObserver and provides reactive width updates. This is the recommended way to provide width to the grid.
|
|
335
|
+
|
|
336
|
+
**Why use it instead of WidthProvider?**
|
|
337
|
+
|
|
338
|
+
- Hooks are more composable and easier to test
|
|
339
|
+
- No HOC wrapper means simpler component tree
|
|
340
|
+
- Explicit control over when to render (via `mounted`)
|
|
341
|
+
- Works better with SSR
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
import { useContainerWidth } from "react-grid-layout";
|
|
345
|
+
|
|
346
|
+
function MyGrid() {
|
|
347
|
+
const { width, containerRef, mounted, measureWidth } = useContainerWidth({
|
|
348
|
+
measureBeforeMount: false, // Set true for SSR
|
|
349
|
+
initialWidth: 1280 // Width before first measurement
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<div ref={containerRef}>{mounted && <ReactGridLayout width={width} />}</div>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Type Definitions:**
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
interface UseContainerWidthOptions {
|
|
362
|
+
/** Delay render until width is measured. Useful for SSR. Default: false */
|
|
363
|
+
measureBeforeMount?: boolean;
|
|
364
|
+
/** Initial width before measurement. Default: 1280 */
|
|
365
|
+
initialWidth?: number;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
interface UseContainerWidthResult {
|
|
369
|
+
/** Current container width in pixels */
|
|
370
|
+
width: number;
|
|
371
|
+
/** Whether the container has been measured at least once */
|
|
372
|
+
mounted: boolean;
|
|
373
|
+
/** Ref to attach to the container element */
|
|
374
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
375
|
+
/** Manually trigger a width measurement */
|
|
376
|
+
measureWidth: () => void;
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### useGridLayout
|
|
381
|
+
|
|
382
|
+
Core layout state management hook. Use this when you need direct control over drag/resize/drop state, or when building a custom grid component.
|
|
383
|
+
|
|
384
|
+
**Why use it instead of the component?**
|
|
385
|
+
|
|
386
|
+
- Full control over layout state and updates
|
|
387
|
+
- Access to drag/resize/drop state for custom UIs
|
|
388
|
+
- Can integrate with external state management
|
|
389
|
+
- Build headless grid implementations
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
import { useGridLayout } from "react-grid-layout";
|
|
393
|
+
|
|
394
|
+
function CustomGrid({ initialLayout }) {
|
|
395
|
+
const {
|
|
396
|
+
layout,
|
|
397
|
+
setLayout,
|
|
398
|
+
dragState,
|
|
399
|
+
resizeState,
|
|
400
|
+
onDragStart,
|
|
401
|
+
onDrag,
|
|
402
|
+
onDragStop,
|
|
403
|
+
onResizeStart,
|
|
404
|
+
onResize,
|
|
405
|
+
onResizeStop,
|
|
406
|
+
containerHeight,
|
|
407
|
+
isInteracting,
|
|
408
|
+
compactor
|
|
409
|
+
} = useGridLayout({
|
|
410
|
+
layout: initialLayout,
|
|
411
|
+
cols: 12,
|
|
412
|
+
compactType: "vertical",
|
|
413
|
+
allowOverlap: false,
|
|
414
|
+
preventCollision: false,
|
|
415
|
+
onLayoutChange: newLayout => console.log("Layout changed:", newLayout)
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Access drag state for custom placeholder rendering
|
|
419
|
+
const placeholder = dragState.activeDrag;
|
|
420
|
+
|
|
421
|
+
// Check if any interaction is happening
|
|
422
|
+
if (isInteracting) {
|
|
423
|
+
// Disable other UI during drag/resize
|
|
258
424
|
}
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<div style={{ height: containerHeight * rowHeight }}>
|
|
428
|
+
{layout.map(item => (
|
|
429
|
+
<div
|
|
430
|
+
key={item.i}
|
|
431
|
+
onMouseDown={() => onDragStart(item.i, item.x, item.y)}
|
|
432
|
+
>
|
|
433
|
+
{item.i}
|
|
434
|
+
</div>
|
|
435
|
+
))}
|
|
436
|
+
{placeholder && <div className="placeholder" />}
|
|
437
|
+
</div>
|
|
438
|
+
);
|
|
259
439
|
}
|
|
260
440
|
```
|
|
261
441
|
|
|
262
|
-
|
|
442
|
+
**Type Definitions:**
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
interface UseGridLayoutOptions {
|
|
446
|
+
/** Initial layout */
|
|
447
|
+
layout: Layout;
|
|
448
|
+
/** Number of columns */
|
|
449
|
+
cols: number;
|
|
450
|
+
/** Compaction type: 'vertical', 'horizontal', or null */
|
|
451
|
+
compactType?: CompactType;
|
|
452
|
+
/** Allow items to overlap (stack on top of each other) */
|
|
453
|
+
allowOverlap?: boolean;
|
|
454
|
+
/** Block movement into occupied space instead of pushing items (no effect if allowOverlap is true) */
|
|
455
|
+
preventCollision?: boolean;
|
|
456
|
+
/** Called when layout changes */
|
|
457
|
+
onLayoutChange?: (layout: Layout) => void;
|
|
458
|
+
}
|
|
263
459
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
460
|
+
interface UseGridLayoutResult {
|
|
461
|
+
/** Current layout */
|
|
462
|
+
layout: Layout;
|
|
463
|
+
/** Set layout directly */
|
|
464
|
+
setLayout: (layout: Layout) => void;
|
|
465
|
+
/** Current drag state (activeDrag, oldDragItem, oldLayout) */
|
|
466
|
+
dragState: DragState;
|
|
467
|
+
/** Current resize state (resizing, oldResizeItem, oldLayout) */
|
|
468
|
+
resizeState: ResizeState;
|
|
469
|
+
/** Current drop state (droppingDOMNode, droppingPosition) */
|
|
470
|
+
dropState: DropState;
|
|
471
|
+
/** Start dragging an item */
|
|
472
|
+
onDragStart: (itemId: string, x: number, y: number) => LayoutItem | null;
|
|
473
|
+
/** Update drag position */
|
|
474
|
+
onDrag: (itemId: string, x: number, y: number) => void;
|
|
475
|
+
/** Stop dragging */
|
|
476
|
+
onDragStop: (itemId: string, x: number, y: number) => void;
|
|
477
|
+
/** Start resizing an item */
|
|
478
|
+
onResizeStart: (itemId: string) => LayoutItem | null;
|
|
479
|
+
/** Update resize dimensions */
|
|
480
|
+
onResize: (
|
|
481
|
+
itemId: string,
|
|
482
|
+
w: number,
|
|
483
|
+
h: number,
|
|
484
|
+
x?: number,
|
|
485
|
+
y?: number
|
|
486
|
+
) => void;
|
|
487
|
+
/** Stop resizing */
|
|
488
|
+
onResizeStop: (itemId: string, w: number, h: number) => void;
|
|
489
|
+
/** Handle external drag over */
|
|
490
|
+
onDropDragOver: (
|
|
491
|
+
droppingItem: LayoutItem,
|
|
492
|
+
position: DroppingPosition
|
|
493
|
+
) => void;
|
|
494
|
+
/** Handle external drag leave */
|
|
495
|
+
onDropDragLeave: () => void;
|
|
496
|
+
/** Complete external drop */
|
|
497
|
+
onDrop: (droppingItem: LayoutItem) => void;
|
|
498
|
+
/** Container height in grid rows */
|
|
499
|
+
containerHeight: number;
|
|
500
|
+
/** Whether any drag/resize/drop is active */
|
|
501
|
+
isInteracting: boolean;
|
|
502
|
+
/** The compactor being used */
|
|
503
|
+
compactor: Compactor;
|
|
504
|
+
}
|
|
505
|
+
```
|
|
267
506
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
507
|
+
### useResponsiveLayout
|
|
508
|
+
|
|
509
|
+
Manages responsive breakpoints and generates layouts for different screen sizes. Use this when building a custom responsive grid.
|
|
510
|
+
|
|
511
|
+
**Why use it instead of the Responsive component?**
|
|
512
|
+
|
|
513
|
+
- Direct access to current breakpoint
|
|
514
|
+
- Control over layout generation for new breakpoints
|
|
515
|
+
- Can update layouts for specific breakpoints
|
|
516
|
+
- Build custom breakpoint UIs
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
import { useContainerWidth, useResponsiveLayout } from "react-grid-layout";
|
|
520
|
+
|
|
521
|
+
function CustomResponsiveGrid() {
|
|
522
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
523
|
+
|
|
524
|
+
const {
|
|
525
|
+
layout, // Current layout for active breakpoint
|
|
526
|
+
layouts, // All layouts by breakpoint
|
|
527
|
+
breakpoint, // Current active breakpoint ('lg', 'md', etc.)
|
|
528
|
+
cols, // Column count for current breakpoint
|
|
529
|
+
setLayoutForBreakpoint,
|
|
530
|
+
setLayouts,
|
|
531
|
+
sortedBreakpoints
|
|
532
|
+
} = useResponsiveLayout({
|
|
533
|
+
width,
|
|
534
|
+
breakpoints: { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 },
|
|
535
|
+
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 },
|
|
536
|
+
layouts: {
|
|
537
|
+
lg: [{ i: "1", x: 0, y: 0, w: 2, h: 2 }],
|
|
538
|
+
md: [{ i: "1", x: 0, y: 0, w: 3, h: 2 }]
|
|
539
|
+
},
|
|
540
|
+
compactType: "vertical",
|
|
541
|
+
onBreakpointChange: (bp, cols) =>
|
|
542
|
+
console.log(`Now at ${bp} (${cols} cols)`),
|
|
543
|
+
onLayoutChange: (layout, allLayouts) => saveToServer(allLayouts)
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
// Show current breakpoint in UI
|
|
547
|
+
return (
|
|
548
|
+
<div ref={containerRef}>
|
|
549
|
+
<div>
|
|
550
|
+
Current breakpoint: {breakpoint} ({cols} columns)
|
|
551
|
+
</div>
|
|
552
|
+
{mounted && (
|
|
553
|
+
<GridLayout width={width} cols={cols} layout={layout}>
|
|
554
|
+
{/* children */}
|
|
555
|
+
</GridLayout>
|
|
556
|
+
)}
|
|
557
|
+
</div>
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
```
|
|
271
561
|
|
|
272
|
-
|
|
562
|
+
**Type Definitions:**
|
|
563
|
+
|
|
564
|
+
```ts
|
|
565
|
+
interface UseResponsiveLayoutOptions<B extends string = DefaultBreakpoints> {
|
|
566
|
+
/** Current container width */
|
|
567
|
+
width: number;
|
|
568
|
+
/** Breakpoint definitions (name → min-width). Default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0} */
|
|
569
|
+
breakpoints?: Record<B, number>;
|
|
570
|
+
/** Column counts per breakpoint. Default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2} */
|
|
571
|
+
cols?: Record<B, number>;
|
|
572
|
+
/** Layouts for each breakpoint */
|
|
573
|
+
layouts?: Partial<Record<B, Layout>>;
|
|
574
|
+
/** Compaction type */
|
|
575
|
+
compactType?: "vertical" | "horizontal" | null;
|
|
576
|
+
/** Called when breakpoint changes */
|
|
577
|
+
onBreakpointChange?: (newBreakpoint: B, cols: number) => void;
|
|
578
|
+
/** Called when layout changes */
|
|
579
|
+
onLayoutChange?: (layout: Layout, layouts: Record<B, Layout>) => void;
|
|
580
|
+
/** Called when width changes */
|
|
581
|
+
onWidthChange?: (
|
|
582
|
+
width: number,
|
|
583
|
+
margin: [number, number],
|
|
584
|
+
cols: number,
|
|
585
|
+
padding: [number, number] | null
|
|
586
|
+
) => void;
|
|
587
|
+
}
|
|
273
588
|
|
|
274
|
-
|
|
589
|
+
interface UseResponsiveLayoutResult<B extends string = DefaultBreakpoints> {
|
|
590
|
+
/** Current layout for the active breakpoint */
|
|
591
|
+
layout: Layout;
|
|
592
|
+
/** All layouts by breakpoint */
|
|
593
|
+
layouts: Partial<Record<B, Layout>>;
|
|
594
|
+
/** Current active breakpoint */
|
|
595
|
+
breakpoint: B;
|
|
596
|
+
/** Column count for the current breakpoint */
|
|
597
|
+
cols: number;
|
|
598
|
+
/** Update layout for a specific breakpoint */
|
|
599
|
+
setLayoutForBreakpoint: (breakpoint: B, layout: Layout) => void;
|
|
600
|
+
/** Update all layouts */
|
|
601
|
+
setLayouts: (layouts: Partial<Record<B, Layout>>) => void;
|
|
602
|
+
/** Sorted array of breakpoint names (smallest to largest) */
|
|
603
|
+
sortedBreakpoints: B[];
|
|
604
|
+
}
|
|
275
605
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
// Basic props
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
// This allows setting the initial width on the server side.
|
|
282
|
-
// This is required unless using the HOC <WidthProvider> or similar
|
|
283
|
-
width: number,
|
|
284
|
-
|
|
285
|
-
// If true, the container height swells and contracts to fit contents
|
|
286
|
-
autoSize: ?boolean = true,
|
|
287
|
-
|
|
288
|
-
// Number of columns in this layout.
|
|
289
|
-
cols: ?number = 12,
|
|
290
|
-
|
|
291
|
-
// A CSS selector for tags that will not be draggable.
|
|
292
|
-
// For example: draggableCancel:'.MyNonDraggableAreaClassName'
|
|
293
|
-
// If you forget the leading . it will not work.
|
|
294
|
-
// .react-resizable-handle" is always prepended to this value.
|
|
295
|
-
draggableCancel: ?string = '',
|
|
296
|
-
|
|
297
|
-
// A CSS selector for tags that will act as the draggable handle.
|
|
298
|
-
// For example: draggableHandle:'.MyDragHandleClassName'
|
|
299
|
-
// If you forget the leading . it will not work.
|
|
300
|
-
draggableHandle: ?string = '',
|
|
301
|
-
|
|
302
|
-
// Compaction type.
|
|
303
|
-
compactType: ?('vertical' | 'horizontal' | null) = 'vertical';
|
|
304
|
-
|
|
305
|
-
// Layout is an array of objects with the format:
|
|
306
|
-
// The index into the layout must match the key used on each item component.
|
|
307
|
-
// If you choose to use custom keys, you can specify that key in the layout
|
|
308
|
-
// array objects using the `i` prop.
|
|
309
|
-
layout: ?Array<{i?: string, x: number, y: number, w: number, h: number}> = null, // If not provided, use data-grid props on children
|
|
310
|
-
|
|
311
|
-
// Margin between items [x, y] in px.
|
|
312
|
-
margin: ?[number, number] = [10, 10],
|
|
313
|
-
|
|
314
|
-
// Padding inside the container [x, y] in px
|
|
315
|
-
containerPadding: ?[number, number] = margin,
|
|
316
|
-
|
|
317
|
-
// Rows have a static height, but you can change this based on breakpoints
|
|
318
|
-
// if you like.
|
|
319
|
-
rowHeight: ?number = 150,
|
|
320
|
-
|
|
321
|
-
// Configuration of a dropping element. Dropping element is a "virtual" element
|
|
322
|
-
// which appears when you drag over some element from outside.
|
|
323
|
-
// It can be changed by passing specific parameters:
|
|
324
|
-
// i - id of an element
|
|
325
|
-
// w - width of an element
|
|
326
|
-
// h - height of an element
|
|
327
|
-
droppingItem?: { i: string, w: number, h: number }
|
|
328
|
-
|
|
329
|
-
//
|
|
330
|
-
// Flags
|
|
331
|
-
//
|
|
332
|
-
isDraggable: ?boolean = true,
|
|
333
|
-
isResizable: ?boolean = true,
|
|
334
|
-
isBounded: ?boolean = false,
|
|
335
|
-
// Uses CSS3 translate() instead of position top/left.
|
|
336
|
-
// This makes about 6x faster paint performance
|
|
337
|
-
useCSSTransforms: ?boolean = true,
|
|
338
|
-
// If parent DOM node of ResponsiveReactGridLayout or ReactGridLayout has "transform: scale(n)" css property,
|
|
339
|
-
// we should set scale coefficient to avoid render artefacts while dragging.
|
|
340
|
-
transformScale: ?number = 1,
|
|
341
|
-
|
|
342
|
-
// If true, grid can be placed one over the other.
|
|
343
|
-
// If set, implies `preventCollision`.
|
|
344
|
-
allowOverlap: ?boolean = false,
|
|
345
|
-
|
|
346
|
-
// If true, grid items won't change position when being
|
|
347
|
-
// dragged over. If `allowOverlap` is still false,
|
|
348
|
-
// this simply won't allow one to drop on an existing object.
|
|
349
|
-
preventCollision: ?boolean = false,
|
|
350
|
-
|
|
351
|
-
// If true, droppable elements (with `draggable={true}` attribute)
|
|
352
|
-
// can be dropped on the grid. It triggers "onDrop" callback
|
|
353
|
-
// with position and event object as parameters.
|
|
354
|
-
// It can be useful for dropping an element in a specific position
|
|
355
|
-
//
|
|
356
|
-
// NOTE: In case of using Firefox you should add
|
|
357
|
-
// `onDragStart={e => e.dataTransfer.setData('text/plain', '')}` attribute
|
|
358
|
-
// along with `draggable={true}` otherwise this feature will work incorrect.
|
|
359
|
-
// onDragStart attribute is required for Firefox for a dragging initialization
|
|
360
|
-
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=568313
|
|
361
|
-
isDroppable: ?boolean = false,
|
|
362
|
-
// Defines which resize handles should be rendered.
|
|
363
|
-
// Allows for any combination of:
|
|
364
|
-
// 's' - South handle (bottom-center)
|
|
365
|
-
// 'w' - West handle (left-center)
|
|
366
|
-
// 'e' - East handle (right-center)
|
|
367
|
-
// 'n' - North handle (top-center)
|
|
368
|
-
// 'sw' - Southwest handle (bottom-left)
|
|
369
|
-
// 'nw' - Northwest handle (top-left)
|
|
370
|
-
// 'se' - Southeast handle (bottom-right)
|
|
371
|
-
// 'ne' - Northeast handle (top-right)
|
|
372
|
-
//
|
|
373
|
-
// Note that changing this property dynamically does not work due to a restriction in react-resizable.
|
|
374
|
-
resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],
|
|
375
|
-
// Custom component for resize handles
|
|
376
|
-
// See `handle` as used in https://github.com/react-grid-layout/react-resizable#resize-handle
|
|
377
|
-
// Your component should have the class `.react-resizable-handle`, or you should add your custom
|
|
378
|
-
// class to the `draggableCancel` prop.
|
|
379
|
-
resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>),
|
|
380
|
-
|
|
381
|
-
//
|
|
382
|
-
// Callbacks
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
// Callback so you can save the layout.
|
|
386
|
-
// Calls back with (currentLayout) after every drag or resize stop.
|
|
387
|
-
onLayoutChange: (layout: Layout) => void,
|
|
388
|
-
|
|
389
|
-
//
|
|
390
|
-
// All callbacks below have signature (layout, oldItem, newItem, placeholder, e, element).
|
|
391
|
-
// 'start' and 'stop' callbacks pass `undefined` for 'placeholder'.
|
|
392
|
-
//
|
|
393
|
-
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
|
|
394
|
-
placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,
|
|
395
|
-
|
|
396
|
-
// Calls when drag starts.
|
|
397
|
-
onDragStart: ItemCallback,
|
|
398
|
-
// Calls on each drag movement.
|
|
399
|
-
onDrag: ItemCallback,
|
|
400
|
-
// Calls when drag is complete.
|
|
401
|
-
onDragStop: ItemCallback,
|
|
402
|
-
// Calls when resize starts.
|
|
403
|
-
onResizeStart: ItemCallback,
|
|
404
|
-
// Calls when resize movement happens.
|
|
405
|
-
onResize: ItemCallback,
|
|
406
|
-
// Calls when resize is complete.
|
|
407
|
-
onResizeStop: ItemCallback,
|
|
408
|
-
|
|
409
|
-
//
|
|
410
|
-
// Dropover functionality
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
// Calls when an element has been dropped into the grid from outside.
|
|
414
|
-
onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void,
|
|
415
|
-
// Calls when an element is being dragged over the grid from outside as above.
|
|
416
|
-
// This callback should return an object to dynamically change the droppingItem size
|
|
417
|
-
// Return false to short-circuit the dragover
|
|
418
|
-
onDropDragOver: (e: DragOverEvent) => ?({|w?: number, h?: number|} | false),
|
|
419
|
-
|
|
420
|
-
// Ref for getting a reference for the grid's wrapping div.
|
|
421
|
-
// You can use this instead of a regular ref and the deprecated `ReactDOM.findDOMNode()`` function.
|
|
422
|
-
// Note that this type is React.Ref<HTMLDivElement> in TypeScript, Flow has a bug here
|
|
423
|
-
// https://github.com/facebook/flow/issues/8671#issuecomment-862634865
|
|
424
|
-
innerRef: {current: null | HTMLDivElement},
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### Responsive Grid Layout Props
|
|
428
|
-
|
|
429
|
-
The responsive grid layout can be used instead. It supports all of the props above, excepting `layout`.
|
|
430
|
-
The new properties and changes are:
|
|
606
|
+
type DefaultBreakpoints = "lg" | "md" | "sm" | "xs" | "xxs";
|
|
607
|
+
```
|
|
431
608
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
609
|
+
## API Reference
|
|
610
|
+
|
|
611
|
+
### ReactGridLayout Props
|
|
612
|
+
|
|
613
|
+
The v2 API uses composable configuration interfaces for cleaner prop organization:
|
|
614
|
+
|
|
615
|
+
```ts
|
|
616
|
+
interface ReactGridLayoutProps {
|
|
617
|
+
// Required
|
|
618
|
+
children: React.ReactNode;
|
|
619
|
+
width: number; // Container width in pixels
|
|
620
|
+
|
|
621
|
+
// Configuration interfaces (see below for details)
|
|
622
|
+
gridConfig?: Partial<GridConfig>; // Grid measurement settings
|
|
623
|
+
dragConfig?: Partial<DragConfig>; // Drag behavior settings
|
|
624
|
+
resizeConfig?: Partial<ResizeConfig>; // Resize behavior settings
|
|
625
|
+
dropConfig?: Partial<DropConfig>; // External drop settings
|
|
626
|
+
positionStrategy?: PositionStrategy; // CSS positioning strategy
|
|
627
|
+
compactor?: Compactor; // Layout compaction strategy
|
|
628
|
+
|
|
629
|
+
// Layout data
|
|
630
|
+
layout?: Layout; // Layout definition
|
|
631
|
+
droppingItem?: LayoutItem; // Item configuration when dropping from outside
|
|
632
|
+
|
|
633
|
+
// Container
|
|
634
|
+
autoSize?: boolean; // Auto-size container height (default: true)
|
|
635
|
+
className?: string;
|
|
636
|
+
style?: React.CSSProperties;
|
|
637
|
+
innerRef?: React.Ref<HTMLDivElement>;
|
|
638
|
+
|
|
639
|
+
// Callbacks
|
|
640
|
+
onLayoutChange?: (layout: Layout) => void;
|
|
641
|
+
onDragStart?: EventCallback;
|
|
642
|
+
onDrag?: EventCallback;
|
|
643
|
+
onDragStop?: EventCallback;
|
|
644
|
+
onResizeStart?: EventCallback;
|
|
645
|
+
onResize?: EventCallback;
|
|
646
|
+
onResizeStop?: EventCallback;
|
|
647
|
+
onDrop?: (layout: Layout, item: LayoutItem | undefined, e: Event) => void;
|
|
648
|
+
onDropDragOver?: (e: DragEvent) => { w?: number; h?: number } | false | void;
|
|
649
|
+
}
|
|
650
|
+
```
|
|
436
651
|
|
|
437
|
-
|
|
438
|
-
cols: ?Object = {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
|
|
652
|
+
### GridConfig
|
|
439
653
|
|
|
654
|
+
Grid measurement configuration:
|
|
440
655
|
|
|
441
|
-
|
|
442
|
-
|
|
656
|
+
```ts
|
|
657
|
+
interface GridConfig {
|
|
658
|
+
cols: number; // Number of columns (default: 12)
|
|
659
|
+
rowHeight: number; // Row height in pixels (default: 150)
|
|
660
|
+
margin: [number, number]; // [x, y] margin between items (default: [10, 10])
|
|
661
|
+
containerPadding: [number, number] | null; // Container padding (default: null, uses margin)
|
|
662
|
+
maxRows: number; // Maximum rows (default: Infinity)
|
|
663
|
+
}
|
|
664
|
+
```
|
|
443
665
|
|
|
666
|
+
### DragConfig
|
|
444
667
|
|
|
445
|
-
|
|
446
|
-
containerPadding: [number, number] | {[breakpoint: $Keys<breakpoints>]: [number, number]},
|
|
668
|
+
Drag behavior configuration:
|
|
447
669
|
|
|
670
|
+
```ts
|
|
671
|
+
interface DragConfig {
|
|
672
|
+
enabled: boolean; // Enable dragging (default: true)
|
|
673
|
+
bounded: boolean; // Keep items within container (default: false)
|
|
674
|
+
handle?: string; // CSS selector for drag handle
|
|
675
|
+
cancel?: string; // CSS selector to cancel dragging
|
|
676
|
+
threshold: number; // Pixels to move before drag starts (default: 3)
|
|
677
|
+
}
|
|
678
|
+
```
|
|
448
679
|
|
|
449
|
-
|
|
450
|
-
// e.g. {lg: Layout, md: Layout, ...}
|
|
451
|
-
layouts: {[key: $Keys<breakpoints>]: Layout},
|
|
680
|
+
### ResizeConfig
|
|
452
681
|
|
|
453
|
-
|
|
454
|
-
// Callbacks
|
|
455
|
-
//
|
|
682
|
+
Resize behavior configuration:
|
|
456
683
|
|
|
457
|
-
|
|
458
|
-
|
|
684
|
+
```ts
|
|
685
|
+
interface ResizeConfig {
|
|
686
|
+
enabled: boolean; // Enable resizing (default: true)
|
|
687
|
+
handles: ResizeHandleAxis[]; // Handle positions (default: ['se'])
|
|
688
|
+
handleComponent?: React.ReactNode | ((axis, ref) => React.ReactNode);
|
|
689
|
+
}
|
|
690
|
+
```
|
|
459
691
|
|
|
460
|
-
|
|
461
|
-
// AllLayouts are keyed by breakpoint.
|
|
462
|
-
onLayoutChange: (currentLayout: Layout, allLayouts: {[key: $Keys<breakpoints>]: Layout}) => void,
|
|
692
|
+
### DropConfig
|
|
463
693
|
|
|
464
|
-
|
|
465
|
-
onWidthChange: (containerWidth: number, margin: [number, number], cols: number, containerPadding: [number, number]) => void;
|
|
694
|
+
External drop configuration:
|
|
466
695
|
|
|
696
|
+
```ts
|
|
697
|
+
interface DropConfig {
|
|
698
|
+
enabled: boolean; // Allow external drops (default: false)
|
|
699
|
+
defaultItem: { w: number; h: number }; // Default size (default: { w: 1, h: 1 })
|
|
700
|
+
onDragOver?: (e: DragEvent) => { w?: number; h?: number } | false | void;
|
|
701
|
+
}
|
|
467
702
|
```
|
|
468
703
|
|
|
469
|
-
###
|
|
704
|
+
### PositionStrategy
|
|
470
705
|
|
|
471
|
-
|
|
472
|
-
build a layout array (as in the first example above), or attach this object as the `data-grid` property
|
|
473
|
-
to each of your child elements (as in the second example).
|
|
706
|
+
CSS positioning strategy. Built-in options:
|
|
474
707
|
|
|
475
|
-
|
|
708
|
+
```ts
|
|
709
|
+
import {
|
|
710
|
+
transformStrategy, // Default: use CSS transforms
|
|
711
|
+
absoluteStrategy, // Use top/left positioning
|
|
712
|
+
createScaledStrategy // For scaled containers
|
|
713
|
+
} from "react-grid-layout/core";
|
|
476
714
|
|
|
477
|
-
|
|
478
|
-
|
|
715
|
+
// Example: scaled container
|
|
716
|
+
<div style={{ transform: 'scale(0.5)' }}>
|
|
717
|
+
<ReactGridLayout positionStrategy={createScaledStrategy(0.5)} ... />
|
|
718
|
+
</div>
|
|
719
|
+
```
|
|
479
720
|
|
|
480
|
-
|
|
721
|
+
### Compactor
|
|
481
722
|
|
|
482
|
-
|
|
483
|
-
is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions
|
|
484
|
-
are out of range.
|
|
723
|
+
Layout compaction strategy. Built-in options:
|
|
485
724
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
725
|
+
```ts
|
|
726
|
+
import {
|
|
727
|
+
verticalCompactor, // Default: compact items upward
|
|
728
|
+
horizontalCompactor, // Compact items leftward
|
|
729
|
+
noCompactor, // No compaction (free positioning)
|
|
730
|
+
getCompactor // Factory: getCompactor('vertical', allowOverlap, preventCollision)
|
|
731
|
+
} from "react-grid-layout/core";
|
|
732
|
+
```
|
|
489
733
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
734
|
+
### ResponsiveGridLayout Props
|
|
735
|
+
|
|
736
|
+
Extends `GridLayoutProps` with responsive-specific props:
|
|
737
|
+
|
|
738
|
+
```ts
|
|
739
|
+
interface ResponsiveGridLayoutProps<B extends string = string> {
|
|
740
|
+
// Responsive configuration
|
|
741
|
+
breakpoint?: B; // Current breakpoint (auto-detected)
|
|
742
|
+
breakpoints?: Record<B, number>; // Breakpoint definitions (default: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0})
|
|
743
|
+
cols?: Record<B, number>; // Columns per breakpoint (default: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2})
|
|
744
|
+
layouts?: Record<B, Layout>; // Layouts per breakpoint
|
|
745
|
+
|
|
746
|
+
// Can be fixed or per-breakpoint
|
|
747
|
+
margin?: [number, number] | Partial<Record<B, [number, number]>>;
|
|
748
|
+
containerPadding?:
|
|
749
|
+
| [number, number]
|
|
750
|
+
| Partial<Record<B, [number, number] | null>>
|
|
751
|
+
| null;
|
|
752
|
+
|
|
753
|
+
// Callbacks
|
|
754
|
+
onBreakpointChange?: (newBreakpoint: B, cols: number) => void;
|
|
755
|
+
onLayoutChange?: (layout: Layout, layouts: Record<B, Layout>) => void;
|
|
756
|
+
onWidthChange?: (
|
|
757
|
+
width: number,
|
|
758
|
+
margin: [number, number],
|
|
759
|
+
cols: number,
|
|
760
|
+
padding: [number, number] | null
|
|
761
|
+
) => void;
|
|
517
762
|
}
|
|
518
763
|
```
|
|
519
764
|
|
|
520
|
-
###
|
|
765
|
+
### Layout Item
|
|
766
|
+
|
|
767
|
+
```ts
|
|
768
|
+
interface LayoutItem {
|
|
769
|
+
i: string; // Unique identifier (must match child key)
|
|
770
|
+
x: number; // X position in grid units
|
|
771
|
+
y: number; // Y position in grid units
|
|
772
|
+
w: number; // Width in grid units
|
|
773
|
+
h: number; // Height in grid units
|
|
774
|
+
minW?: number; // Minimum width (default: 0)
|
|
775
|
+
maxW?: number; // Maximum width (default: Infinity)
|
|
776
|
+
minH?: number; // Minimum height (default: 0)
|
|
777
|
+
maxH?: number; // Maximum height (default: Infinity)
|
|
778
|
+
static?: boolean; // If true, not draggable or resizable
|
|
779
|
+
isDraggable?: boolean; // Override grid isDraggable
|
|
780
|
+
isResizable?: boolean; // Override grid isResizable
|
|
781
|
+
isBounded?: boolean; // Override grid isBounded
|
|
782
|
+
resizeHandles?: Array<"s" | "w" | "e" | "n" | "sw" | "nw" | "se" | "ne">;
|
|
783
|
+
}
|
|
784
|
+
```
|
|
521
785
|
|
|
522
|
-
|
|
786
|
+
### Core Utilities
|
|
523
787
|
|
|
524
|
-
|
|
788
|
+
Import pure layout functions from `react-grid-layout/core`:
|
|
525
789
|
|
|
526
|
-
|
|
790
|
+
```ts
|
|
791
|
+
import {
|
|
792
|
+
compact,
|
|
793
|
+
moveElement,
|
|
794
|
+
collides,
|
|
795
|
+
getFirstCollision,
|
|
796
|
+
validateLayout
|
|
797
|
+
// ... and more
|
|
798
|
+
} from "react-grid-layout/core";
|
|
799
|
+
```
|
|
527
800
|
|
|
528
|
-
|
|
801
|
+
## Extending: Custom Compactors & Position Strategies
|
|
802
|
+
|
|
803
|
+
### Creating a Custom Compactor
|
|
804
|
+
|
|
805
|
+
Compactors control how items are arranged after drag/resize. Create your own for custom layouts like masonry, gravity, or shelf-packing.
|
|
806
|
+
|
|
807
|
+
**The Compactor Interface:**
|
|
808
|
+
|
|
809
|
+
```ts
|
|
810
|
+
interface Compactor {
|
|
811
|
+
/** Identifies the compaction type */
|
|
812
|
+
type: "vertical" | "horizontal" | null | string;
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Whether items can overlap each other.
|
|
816
|
+
*
|
|
817
|
+
* When true:
|
|
818
|
+
* - Items can be placed on top of other items
|
|
819
|
+
* - Dragging into another item does NOT push it away
|
|
820
|
+
* - Compaction is skipped after drag/resize
|
|
821
|
+
*
|
|
822
|
+
* Use for: layered dashboards, free-form layouts
|
|
823
|
+
*/
|
|
824
|
+
allowOverlap: boolean;
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Whether to block movement that would cause collision.
|
|
828
|
+
*
|
|
829
|
+
* When true (and allowOverlap is false):
|
|
830
|
+
* - Dragging into another item is blocked (item snaps back)
|
|
831
|
+
* - Other items are NOT pushed away
|
|
832
|
+
* - Only affects movement, not compaction
|
|
833
|
+
*
|
|
834
|
+
* Use with noCompactor for: fixed grids, slot-based layouts
|
|
835
|
+
*
|
|
836
|
+
* Note: Has no effect when allowOverlap is true.
|
|
837
|
+
*/
|
|
838
|
+
preventCollision?: boolean;
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Compact the entire layout.
|
|
842
|
+
* Called after any layout change to fill gaps.
|
|
843
|
+
*
|
|
844
|
+
* @param layout - Array of layout items (clone before mutating!)
|
|
845
|
+
* @param cols - Number of grid columns
|
|
846
|
+
* @returns New compacted layout
|
|
847
|
+
*/
|
|
848
|
+
compact(layout: Layout, cols: number): Layout;
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Handle moving an item.
|
|
852
|
+
* Called during drag to preview the new position.
|
|
853
|
+
*
|
|
854
|
+
* @param layout - Current layout
|
|
855
|
+
* @param item - Item being moved
|
|
856
|
+
* @param x - New X position in grid units
|
|
857
|
+
* @param y - New Y position in grid units
|
|
858
|
+
* @param cols - Number of grid columns
|
|
859
|
+
* @returns Updated layout with item at new position
|
|
860
|
+
*/
|
|
861
|
+
onMove(
|
|
862
|
+
layout: Layout,
|
|
863
|
+
item: LayoutItem,
|
|
864
|
+
x: number,
|
|
865
|
+
y: number,
|
|
866
|
+
cols: number
|
|
867
|
+
): Layout;
|
|
868
|
+
}
|
|
869
|
+
```
|
|
529
870
|
|
|
530
|
-
|
|
871
|
+
**Example: Gravity Compactor (items fall to bottom)**
|
|
872
|
+
|
|
873
|
+
```ts
|
|
874
|
+
import { cloneLayout, cloneLayoutItem, getStatics, bottom } from "react-grid-layout/core";
|
|
875
|
+
|
|
876
|
+
const gravityCompactor: Compactor = {
|
|
877
|
+
type: "gravity",
|
|
878
|
+
allowOverlap: false,
|
|
879
|
+
|
|
880
|
+
compact(layout, cols) {
|
|
881
|
+
const statics = getStatics(layout);
|
|
882
|
+
const maxY = 100; // arbitrary max height
|
|
883
|
+
const out = [];
|
|
884
|
+
|
|
885
|
+
// Sort by Y descending (process bottom items first)
|
|
886
|
+
const sorted = [...layout].sort((a, b) => b.y - a.y);
|
|
887
|
+
|
|
888
|
+
for (const item of sorted) {
|
|
889
|
+
const l = cloneLayoutItem(item);
|
|
890
|
+
|
|
891
|
+
if (!l.static) {
|
|
892
|
+
// Move down as far as possible
|
|
893
|
+
while (l.y < maxY && !collides(l, statics)) {
|
|
894
|
+
l.y++;
|
|
895
|
+
}
|
|
896
|
+
l.y--; // Back up one
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
out.push(l);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return out;
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
onMove(layout, item, x, y, cols) {
|
|
906
|
+
const newLayout = cloneLayout(layout);
|
|
907
|
+
const movedItem = newLayout.find(l => l.i === item.i);
|
|
908
|
+
if (movedItem) {
|
|
909
|
+
movedItem.x = x;
|
|
910
|
+
movedItem.y = y;
|
|
911
|
+
movedItem.moved = true;
|
|
912
|
+
}
|
|
913
|
+
return newLayout;
|
|
914
|
+
}
|
|
915
|
+
};
|
|
531
916
|
|
|
532
|
-
|
|
917
|
+
// Usage
|
|
918
|
+
<GridLayout compactor={gravityCompactor} />
|
|
919
|
+
```
|
|
533
920
|
|
|
534
|
-
|
|
921
|
+
**Example: Single Row Compactor (horizontal shelf)**
|
|
922
|
+
|
|
923
|
+
```ts
|
|
924
|
+
const singleRowCompactor: Compactor = {
|
|
925
|
+
type: "shelf",
|
|
926
|
+
allowOverlap: false,
|
|
927
|
+
|
|
928
|
+
compact(layout, cols) {
|
|
929
|
+
let x = 0;
|
|
930
|
+
const out = [];
|
|
931
|
+
|
|
932
|
+
// Sort by original X position
|
|
933
|
+
const sorted = [...layout].sort((a, b) => a.x - b.x);
|
|
934
|
+
|
|
935
|
+
for (const item of sorted) {
|
|
936
|
+
const l = cloneLayoutItem(item);
|
|
937
|
+
if (!l.static) {
|
|
938
|
+
l.x = x;
|
|
939
|
+
l.y = 0; // All items on row 0
|
|
940
|
+
x += l.w;
|
|
941
|
+
|
|
942
|
+
// Wrap to next row if overflow
|
|
943
|
+
if (x > cols) {
|
|
944
|
+
l.x = 0;
|
|
945
|
+
x = l.w;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
out.push(l);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
return out;
|
|
952
|
+
},
|
|
953
|
+
|
|
954
|
+
onMove(layout, item, x, y, cols) {
|
|
955
|
+
// Same as default - just update position
|
|
956
|
+
const newLayout = cloneLayout(layout);
|
|
957
|
+
const movedItem = newLayout.find(l => l.i === item.i);
|
|
958
|
+
if (movedItem) {
|
|
959
|
+
movedItem.x = x;
|
|
960
|
+
movedItem.y = 0; // Force row 0
|
|
961
|
+
movedItem.moved = true;
|
|
962
|
+
}
|
|
963
|
+
return newLayout;
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
```
|
|
535
967
|
|
|
536
|
-
|
|
968
|
+
**Using Helper Functions:**
|
|
969
|
+
|
|
970
|
+
The core module exports helpers for building compactors:
|
|
971
|
+
|
|
972
|
+
```ts
|
|
973
|
+
import {
|
|
974
|
+
resolveCompactionCollision, // Move items to resolve overlaps
|
|
975
|
+
compactItemVertical, // Compact one item upward
|
|
976
|
+
compactItemHorizontal, // Compact one item leftward
|
|
977
|
+
getFirstCollision, // Find first collision
|
|
978
|
+
collides, // Check if two items collide
|
|
979
|
+
getStatics, // Get static items from layout
|
|
980
|
+
cloneLayout, // Clone layout array
|
|
981
|
+
cloneLayoutItem // Clone single item
|
|
982
|
+
} from "react-grid-layout/core";
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
### Creating a Custom Position Strategy
|
|
986
|
+
|
|
987
|
+
Position strategies control how items are positioned via CSS. Create custom strategies for special transform handling.
|
|
988
|
+
|
|
989
|
+
**The PositionStrategy Interface:**
|
|
990
|
+
|
|
991
|
+
```ts
|
|
992
|
+
interface PositionStrategy {
|
|
993
|
+
/** Type identifier */
|
|
994
|
+
type: "transform" | "absolute" | string;
|
|
995
|
+
|
|
996
|
+
/** Scale factor for coordinate calculations */
|
|
997
|
+
scale: number;
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Generate CSS styles for positioning an item.
|
|
1001
|
+
*
|
|
1002
|
+
* @param pos - Position with top, left, width, height in pixels
|
|
1003
|
+
* @returns CSS properties object
|
|
1004
|
+
*/
|
|
1005
|
+
calcStyle(pos: Position): React.CSSProperties;
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Calculate drag position from mouse coordinates.
|
|
1009
|
+
* Used during drag to convert screen coords to grid coords.
|
|
1010
|
+
*
|
|
1011
|
+
* @param clientX - Mouse X position
|
|
1012
|
+
* @param clientY - Mouse Y position
|
|
1013
|
+
* @param offsetX - Offset from item left edge
|
|
1014
|
+
* @param offsetY - Offset from item top edge
|
|
1015
|
+
* @returns Calculated left/top position
|
|
1016
|
+
*/
|
|
1017
|
+
calcDragPosition(
|
|
1018
|
+
clientX: number,
|
|
1019
|
+
clientY: number,
|
|
1020
|
+
offsetX: number,
|
|
1021
|
+
offsetY: number
|
|
1022
|
+
): { left: number; top: number };
|
|
1023
|
+
}
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
**Example: Rotated Container Strategy**
|
|
1027
|
+
|
|
1028
|
+
```ts
|
|
1029
|
+
const createRotatedStrategy = (angleDegrees: number): PositionStrategy => {
|
|
1030
|
+
const angleRad = (angleDegrees * Math.PI) / 180;
|
|
1031
|
+
const cos = Math.cos(angleRad);
|
|
1032
|
+
const sin = Math.sin(angleRad);
|
|
1033
|
+
|
|
1034
|
+
return {
|
|
1035
|
+
type: "rotated",
|
|
1036
|
+
scale: 1,
|
|
1037
|
+
|
|
1038
|
+
calcStyle(pos) {
|
|
1039
|
+
// Apply rotation to position
|
|
1040
|
+
const rotatedX = pos.left * cos - pos.top * sin;
|
|
1041
|
+
const rotatedY = pos.left * sin + pos.top * cos;
|
|
1042
|
+
|
|
1043
|
+
return {
|
|
1044
|
+
transform: `translate(${rotatedX}px, ${rotatedY}px)`,
|
|
1045
|
+
width: `${pos.width}px`,
|
|
1046
|
+
height: `${pos.height}px`,
|
|
1047
|
+
position: "absolute"
|
|
1048
|
+
};
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
calcDragPosition(clientX, clientY, offsetX, offsetY) {
|
|
1052
|
+
// Reverse the rotation for drag calculations
|
|
1053
|
+
const x = clientX - offsetX;
|
|
1054
|
+
const y = clientY - offsetY;
|
|
1055
|
+
|
|
1056
|
+
return {
|
|
1057
|
+
left: x * cos + y * sin,
|
|
1058
|
+
top: -x * sin + y * cos
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
// Usage: grid inside a rotated container
|
|
1065
|
+
<div style={{ transform: 'rotate(45deg)' }}>
|
|
1066
|
+
<GridLayout positionStrategy={createRotatedStrategy(45)} />
|
|
1067
|
+
</div>
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
**Example: 3D Perspective Strategy**
|
|
1071
|
+
|
|
1072
|
+
```ts
|
|
1073
|
+
const create3DStrategy = (
|
|
1074
|
+
perspective: number,
|
|
1075
|
+
rotateX: number
|
|
1076
|
+
): PositionStrategy => ({
|
|
1077
|
+
type: "3d",
|
|
1078
|
+
scale: 1,
|
|
1079
|
+
|
|
1080
|
+
calcStyle(pos) {
|
|
1081
|
+
return {
|
|
1082
|
+
transform: `
|
|
1083
|
+
perspective(${perspective}px)
|
|
1084
|
+
rotateX(${rotateX}deg)
|
|
1085
|
+
translate3d(${pos.left}px, ${pos.top}px, 0)
|
|
1086
|
+
`,
|
|
1087
|
+
width: `${pos.width}px`,
|
|
1088
|
+
height: `${pos.height}px`,
|
|
1089
|
+
position: "absolute",
|
|
1090
|
+
transformStyle: "preserve-3d"
|
|
1091
|
+
};
|
|
1092
|
+
},
|
|
1093
|
+
|
|
1094
|
+
calcDragPosition(clientX, clientY, offsetX, offsetY) {
|
|
1095
|
+
// Adjust for perspective foreshortening
|
|
1096
|
+
const perspectiveFactor = 1 + clientY / perspective;
|
|
1097
|
+
return {
|
|
1098
|
+
left: (clientX - offsetX) / perspectiveFactor,
|
|
1099
|
+
top: (clientY - offsetY) / perspectiveFactor
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
## Extras
|
|
1106
|
+
|
|
1107
|
+
The `react-grid-layout/extras` entry point provides optional components that extend react-grid-layout. These are tree-shakeable and won't be included in your bundle unless explicitly imported.
|
|
1108
|
+
|
|
1109
|
+
### GridBackground
|
|
1110
|
+
|
|
1111
|
+
Renders an SVG grid background that aligns with GridLayout cells. Use this to visualize the grid structure behind your layout.
|
|
1112
|
+
|
|
1113
|
+
> Based on [PR #2175](https://github.com/react-grid-layout/react-grid-layout/pull/2175) by [@dmj900501](https://github.com/dmj900501).
|
|
1114
|
+
|
|
1115
|
+
```tsx
|
|
1116
|
+
import { GridBackground } from "react-grid-layout/extras";
|
|
1117
|
+
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
|
|
1118
|
+
|
|
1119
|
+
function MyGrid() {
|
|
1120
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
537
1121
|
|
|
538
|
-
```js
|
|
539
|
-
// lib/ReactGridLayout.jsx
|
|
540
|
-
// ...
|
|
541
|
-
shouldComponentUpdate(nextProps: Props, nextState: State) {
|
|
542
1122
|
return (
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
1123
|
+
<div ref={containerRef} style={{ position: "relative" }}>
|
|
1124
|
+
{mounted && (
|
|
1125
|
+
<>
|
|
1126
|
+
<GridBackground
|
|
1127
|
+
width={width}
|
|
1128
|
+
cols={12}
|
|
1129
|
+
rowHeight={30}
|
|
1130
|
+
margin={[10, 10]}
|
|
1131
|
+
rows={10}
|
|
1132
|
+
color="#f0f0f0"
|
|
1133
|
+
borderRadius={4}
|
|
1134
|
+
/>
|
|
1135
|
+
<ReactGridLayout
|
|
1136
|
+
width={width}
|
|
1137
|
+
gridConfig={{ cols: 12, rowHeight: 30, margin: [10, 10] }}
|
|
1138
|
+
>
|
|
1139
|
+
{children}
|
|
1140
|
+
</ReactGridLayout>
|
|
1141
|
+
</>
|
|
1142
|
+
)}
|
|
1143
|
+
</div>
|
|
549
1144
|
);
|
|
550
1145
|
}
|
|
551
|
-
// ...
|
|
552
1146
|
```
|
|
553
1147
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
1148
|
+
**Props:**
|
|
1149
|
+
|
|
1150
|
+
```ts
|
|
1151
|
+
interface GridBackgroundProps {
|
|
1152
|
+
// Required - must match your GridLayout config
|
|
1153
|
+
width: number; // Container width
|
|
1154
|
+
cols: number; // Number of columns
|
|
1155
|
+
rowHeight: number; // Row height in pixels
|
|
1156
|
+
|
|
1157
|
+
// Optional
|
|
1158
|
+
margin?: [number, number]; // Gap between cells (default: [10, 10])
|
|
1159
|
+
containerPadding?: [number, number] | null; // Container padding (default: uses margin)
|
|
1160
|
+
rows?: number | "auto"; // Number of rows to display (default: 10)
|
|
1161
|
+
height?: number; // Used when rows="auto" to calculate row count
|
|
1162
|
+
color?: string; // Cell background color (default: "#e0e0e0")
|
|
1163
|
+
borderRadius?: number; // Cell border radius (default: 4)
|
|
1164
|
+
className?: string; // Additional CSS class
|
|
1165
|
+
style?: React.CSSProperties; // Additional inline styles
|
|
564
1166
|
}
|
|
565
1167
|
```
|
|
566
1168
|
|
|
567
|
-
|
|
1169
|
+
### Fast Compactors
|
|
568
1170
|
|
|
569
|
-
|
|
1171
|
+
For large layouts (200+ items), the standard compactors can become slow due to O(n²) collision resolution. The fast compactors use optimized algorithms with O(n log n) complexity.
|
|
570
1172
|
|
|
571
|
-
|
|
572
|
-
To avoid this you should wrap your WidthProvider in a useMemo:
|
|
1173
|
+
> Based on the "rising tide" algorithm from [PR #2152](https://github.com/react-grid-layout/react-grid-layout/pull/2152) by [@morris](https://github.com/morris).
|
|
573
1174
|
|
|
574
|
-
```
|
|
575
|
-
|
|
1175
|
+
```tsx
|
|
1176
|
+
import {
|
|
1177
|
+
fastVerticalCompactor,
|
|
1178
|
+
fastHorizontalCompactor,
|
|
1179
|
+
fastVerticalOverlapCompactor,
|
|
1180
|
+
fastHorizontalOverlapCompactor
|
|
1181
|
+
} from "react-grid-layout/extras";
|
|
1182
|
+
|
|
1183
|
+
<ReactGridLayout
|
|
1184
|
+
compactor={fastVerticalCompactor}
|
|
1185
|
+
// or compactor={fastHorizontalCompactor}
|
|
1186
|
+
layout={layout}
|
|
1187
|
+
width={width}
|
|
1188
|
+
/>;
|
|
576
1189
|
```
|
|
577
1190
|
|
|
578
|
-
|
|
1191
|
+
**Performance Benchmarks:**
|
|
1192
|
+
|
|
1193
|
+
| Items | Standard Vertical | Fast Vertical | Speedup |
|
|
1194
|
+
| ----- | ----------------- | ------------- | ------- |
|
|
1195
|
+
| 50 | 112 µs | 19 µs | **6x** |
|
|
1196
|
+
| 100 | 203 µs | 36 µs | **6x** |
|
|
1197
|
+
| 200 | 821 µs | 51 µs | **16x** |
|
|
1198
|
+
| 500 | 5.7 ms | 129 µs | **45x** |
|
|
1199
|
+
|
|
1200
|
+
| Items | Standard Horizontal | Fast Horizontal | Speedup |
|
|
1201
|
+
| ----- | ------------------- | --------------- | ------- |
|
|
1202
|
+
| 50 | 164 µs | 12 µs | **14x** |
|
|
1203
|
+
| 100 | 477 µs | 25 µs | **19x** |
|
|
1204
|
+
| 200 | 1.1 ms | 42 µs | **26x** |
|
|
1205
|
+
| 500 | 9.5 ms | 128 µs | **74x** |
|
|
1206
|
+
|
|
1207
|
+
**Correctness:**
|
|
1208
|
+
|
|
1209
|
+
The fast compactors produce layouts identical to the standard compactors:
|
|
1210
|
+
|
|
1211
|
+
- **Vertical**: 0% height difference on deterministic 100-item layouts
|
|
1212
|
+
- **Horizontal**: 0% width difference on deterministic 100-item layouts
|
|
1213
|
+
- Both pass all correctness tests: no overlaps, idempotent, static item handling
|
|
1214
|
+
|
|
1215
|
+
**When to use:**
|
|
1216
|
+
|
|
1217
|
+
- Use fast compactors for dashboards with 200+ widgets
|
|
1218
|
+
- For smaller layouts (<100 items), standard compactors work equally well
|
|
1219
|
+
- Both standard and fast compactors produce valid, non-overlapping layouts
|
|
1220
|
+
|
|
1221
|
+
### calcGridCellDimensions (Core Utility)
|
|
1222
|
+
|
|
1223
|
+
For building custom grid overlays or backgrounds, use the `calcGridCellDimensions` utility from `react-grid-layout/core`:
|
|
1224
|
+
|
|
1225
|
+
```ts
|
|
1226
|
+
import { calcGridCellDimensions } from "react-grid-layout/core";
|
|
1227
|
+
|
|
1228
|
+
const dims = calcGridCellDimensions({
|
|
1229
|
+
width: 1200,
|
|
1230
|
+
cols: 12,
|
|
1231
|
+
rowHeight: 30,
|
|
1232
|
+
margin: [10, 10],
|
|
1233
|
+
containerPadding: [20, 20]
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
// dims = {
|
|
1237
|
+
// cellWidth: 88.33, // Width of each cell
|
|
1238
|
+
// cellHeight: 30, // Height of each cell (= rowHeight)
|
|
1239
|
+
// offsetX: 20, // Left padding
|
|
1240
|
+
// offsetY: 20, // Top padding
|
|
1241
|
+
// gapX: 10, // Horizontal gap between cells
|
|
1242
|
+
// gapY: 10, // Vertical gap between cells
|
|
1243
|
+
// cols: 12, // Column count
|
|
1244
|
+
// containerWidth: 1200
|
|
1245
|
+
// }
|
|
1246
|
+
```
|
|
579
1247
|
|
|
580
|
-
|
|
1248
|
+
This is useful for building custom visualizations, snap-to-grid functionality, or integrating with canvas/WebGL renderers.
|
|
581
1249
|
|
|
582
|
-
|
|
583
|
-
2. Forward `style`,`className`, `onMouseDown`, `onMouseUp` and `onTouchEnd` to that same DOM node.
|
|
1250
|
+
## Performance
|
|
584
1251
|
|
|
585
|
-
|
|
1252
|
+
### Memoize Children
|
|
1253
|
+
|
|
1254
|
+
The grid compares children by reference. Memoize them for better performance:
|
|
1255
|
+
|
|
1256
|
+
```tsx
|
|
1257
|
+
function MyGrid({ count, width }) {
|
|
1258
|
+
const children = useMemo(() => {
|
|
1259
|
+
return Array.from({ length: count }, (_, i) => (
|
|
1260
|
+
<div
|
|
1261
|
+
key={i}
|
|
1262
|
+
data-grid={{ x: i % 12, y: Math.floor(i / 12), w: 1, h: 1 }}
|
|
1263
|
+
/>
|
|
1264
|
+
));
|
|
1265
|
+
}, [count]);
|
|
586
1266
|
|
|
587
|
-
```js
|
|
588
|
-
const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
|
|
589
1267
|
return (
|
|
590
|
-
<
|
|
591
|
-
{
|
|
592
|
-
|
|
593
|
-
</div>
|
|
1268
|
+
<ReactGridLayout width={width} gridConfig={{ cols: 12 }}>
|
|
1269
|
+
{children}
|
|
1270
|
+
</ReactGridLayout>
|
|
594
1271
|
);
|
|
595
|
-
}
|
|
1272
|
+
}
|
|
596
1273
|
```
|
|
597
1274
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1275
|
+
### Avoid Creating Components in Render (Legacy WidthProvider)
|
|
1276
|
+
|
|
1277
|
+
If using the legacy WidthProvider HOC, don't create the component during render:
|
|
1278
|
+
|
|
1279
|
+
```tsx
|
|
1280
|
+
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
|
|
1281
|
+
|
|
1282
|
+
// Bad - creates new component every render
|
|
1283
|
+
function MyGrid() {
|
|
1284
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
1285
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// Good - create once outside or with useMemo
|
|
1289
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
1290
|
+
|
|
1291
|
+
function MyGrid() {
|
|
1292
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
1293
|
+
}
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
With the v2 API, use `useContainerWidth` hook instead to avoid this issue entirely.
|
|
1297
|
+
|
|
1298
|
+
## Custom Child Components
|
|
1299
|
+
|
|
1300
|
+
Grid children must forward refs and certain props:
|
|
1301
|
+
|
|
1302
|
+
```tsx
|
|
1303
|
+
const CustomItem = forwardRef<HTMLDivElement, CustomItemProps>(
|
|
1304
|
+
(
|
|
1305
|
+
{
|
|
1306
|
+
style,
|
|
1307
|
+
className,
|
|
1308
|
+
onMouseDown,
|
|
1309
|
+
onMouseUp,
|
|
1310
|
+
onTouchEnd,
|
|
1311
|
+
children,
|
|
1312
|
+
...props
|
|
1313
|
+
},
|
|
1314
|
+
ref
|
|
1315
|
+
) => {
|
|
1316
|
+
return (
|
|
1317
|
+
<div
|
|
1318
|
+
ref={ref}
|
|
1319
|
+
style={style}
|
|
1320
|
+
className={className}
|
|
1321
|
+
onMouseDown={onMouseDown}
|
|
1322
|
+
onMouseUp={onMouseUp}
|
|
1323
|
+
onTouchEnd={onTouchEnd}
|
|
1324
|
+
>
|
|
1325
|
+
{children}
|
|
1326
|
+
</div>
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
);
|
|
1330
|
+
```
|
|
601
1331
|
|
|
602
1332
|
## Contribute
|
|
603
1333
|
|
|
@@ -605,19 +1335,3 @@ If you have a feature request, please add it as an issue or make a pull request.
|
|
|
605
1335
|
|
|
606
1336
|
If you have a bug to report, please reproduce the bug in [CodeSandbox](https://codesandbox.io/s/staging-bush-3lvt7?file=/src/ShowcaseLayout.js) to help
|
|
607
1337
|
us easily isolate it.
|
|
608
|
-
|
|
609
|
-
## TODO List
|
|
610
|
-
|
|
611
|
-
- [x] Basic grid layout
|
|
612
|
-
- [x] Fluid grid layout
|
|
613
|
-
- [x] Grid packing
|
|
614
|
-
- [x] Draggable grid items
|
|
615
|
-
- [x] Live grid packing while dragging
|
|
616
|
-
- [x] Resizable grid items
|
|
617
|
-
- [x] Layouts per responsive breakpoint
|
|
618
|
-
- [x] Define grid attributes on children themselves (`data-grid` key)
|
|
619
|
-
- [x] Static elements
|
|
620
|
-
- [x] Persistent id per item for predictable localstorage restores, even when # items changes
|
|
621
|
-
- [x] Min/max w/h per item
|
|
622
|
-
- [x] Resizable handles on other corners
|
|
623
|
-
- [ ] Configurable w/h per breakpoint
|