react-grid-layout 1.5.2 → 2.0.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 +1075 -432
- package/css/styles.css +6 -4
- package/dist/ResponsiveGridLayout-CH4s0tKj.d.ts +124 -0
- package/dist/ResponsiveGridLayout-D6lFRE3f.d.mts +124 -0
- package/dist/calculate-CwYDW8na.d.mts +168 -0
- package/dist/calculate-mgLpNJ5O.d.ts +168 -0
- package/dist/chunk-2KUHNJXF.mjs +90 -0
- package/dist/chunk-2KUHNJXF.mjs.map +1 -0
- package/dist/chunk-3WO4SAYB.js +868 -0
- package/dist/chunk-3WO4SAYB.js.map +1 -0
- package/dist/chunk-4HNUMWQK.mjs +822 -0
- package/dist/chunk-4HNUMWQK.mjs.map +1 -0
- package/dist/chunk-BFTKGAP3.js +1309 -0
- package/dist/chunk-BFTKGAP3.js.map +1 -0
- package/dist/chunk-F6NQPYKT.js +98 -0
- package/dist/chunk-F6NQPYKT.js.map +1 -0
- package/dist/chunk-H5KMDLY3.js +434 -0
- package/dist/chunk-H5KMDLY3.js.map +1 -0
- package/dist/chunk-PBQSHIID.js +4 -0
- package/dist/chunk-PBQSHIID.js.map +1 -0
- package/dist/chunk-R35HZZTA.mjs +1300 -0
- package/dist/chunk-R35HZZTA.mjs.map +1 -0
- package/dist/chunk-ZCXE6SR5.mjs +428 -0
- package/dist/chunk-ZCXE6SR5.mjs.map +1 -0
- package/dist/chunk-ZWN22PS2.mjs +3 -0
- package/dist/chunk-ZWN22PS2.mjs.map +1 -0
- package/dist/core.d.mts +33 -0
- package/dist/core.d.ts +33 -0
- package/dist/core.js +218 -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 +78 -0
- package/dist/extras.d.ts +78 -0
- package/dist/extras.js +91 -0
- package/dist/extras.js.map +1 -0
- package/dist/extras.mjs +89 -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 +319 -0
- package/dist/legacy.js.map +1 -0
- package/dist/legacy.mjs +307 -0
- package/dist/legacy.mjs.map +1 -0
- package/dist/position-BmN1z36J.d.mts +319 -0
- package/dist/position-Dk2b4ZMS.d.ts +319 -0
- package/dist/react.d.mts +351 -0
- package/dist/react.d.ts +351 -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-C4L1ESlm.d.ts +145 -0
- package/dist/responsive-CJxefsYw.d.mts +145 -0
- package/dist/types-Cxf4nHNr.d.mts +375 -0
- package/dist/types-Cxf4nHNr.d.ts +375 -0
- package/index-dev.js +8 -0
- package/package.json +132 -39
- 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 -640
- package/build/ReactGridLayout.js +0 -744
- package/build/ReactGridLayoutPropTypes.js +0 -210
- package/build/ResponsiveReactGridLayout.js +0 -294
- package/build/calculateUtils.js +0 -165
- package/build/components/WidthProvider.js +0 -111
- package/build/fastRGLPropsEqual.js +0 -5
- package/build/responsiveUtils.js +0 -101
- package/build/utils.js +0 -834
- 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 -979
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
|
+
| [Fast compaction](./rfcs/0001-v2-typescript-rewrite.md#4-fast-compaction-algorithm-by-default) | O(n log n) algorithm may differ in edge cases. Use `compact()` from `/core` for exact v1 behavior |
|
|
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
|
|
|
@@ -62,30 +138,21 @@ RGL is React-only and does not require jQuery.
|
|
|
62
138
|
|
|
63
139
|
#### Projects Using React-Grid-Layout
|
|
64
140
|
|
|
141
|
+
- [Basedash](https://www.basedash.com)
|
|
65
142
|
- [BitMEX](https://www.bitmex.com/)
|
|
66
143
|
- [AWS CloudFront Dashboards](https://aws.amazon.com/blogs/aws/cloudwatch-dashboards-create-use-customized-metrics-views/)
|
|
67
144
|
- [Grafana](https://grafana.com/)
|
|
68
145
|
- [Metabase](http://www.metabase.com/)
|
|
69
146
|
- [HubSpot](http://www.hubspot.com)
|
|
70
|
-
- [ComNetViz](http://www.grotto-networking.com/ComNetViz/ComNetViz.html)
|
|
71
|
-
- [Stoplight](https://app.stoplight.io)
|
|
72
|
-
- [Reflect](https://reflect.io)
|
|
73
|
-
- [ez-Dashing](https://github.com/ylacaute/ez-Dashing)
|
|
74
147
|
- [Kibana](https://www.elastic.co/products/kibana)
|
|
75
|
-
- [Graphext](https://graphext.com/)
|
|
76
148
|
- [Monday](https://support.monday.com/hc/en-us/articles/360002187819-What-are-the-Dashboards-)
|
|
77
|
-
- [Quadency](https://quadency.com/)
|
|
78
|
-
- [Hakkiri](https://www.hakkiri.io)
|
|
79
|
-
- [Ubidots](https://help.ubidots.com/en/articles/2400308-create-dashboards-and-widgets)
|
|
80
|
-
- [Statsout](https://statsout.com/)
|
|
81
|
-
- [Datto RMM](https://www.datto.com/uk/products/rmm/)
|
|
82
|
-
- [SquaredUp](https://squaredup.com/)
|
|
83
149
|
|
|
84
150
|
_Know of others? Create a PR to let me know!_
|
|
85
151
|
|
|
86
152
|
## Features
|
|
87
153
|
|
|
88
154
|
- 100% React - no jQuery
|
|
155
|
+
- Full TypeScript support
|
|
89
156
|
- Compatible with server-rendered apps
|
|
90
157
|
- Draggable widgets
|
|
91
158
|
- Resizable widgets
|
|
@@ -97,506 +164,1098 @@ _Know of others? Create a PR to let me know!_
|
|
|
97
164
|
- Responsive breakpoints
|
|
98
165
|
- Separate layouts per responsive breakpoint
|
|
99
166
|
- Grid Items placed using CSS Transforms
|
|
100
|
-
- Performance with CSS Transforms: [on](http://i.imgur.com/FTogpLp.jpg) / [off](http://i.imgur.com/gOveMm8.jpg), note paint (green) as % of time
|
|
101
167
|
- Compatibility with `<React.StrictMode>`
|
|
102
168
|
|
|
103
|
-
| Version
|
|
104
|
-
|
|
|
105
|
-
| >= 0.
|
|
106
|
-
| >= 0.
|
|
107
|
-
| >= 0.10.0 | React 0.14 |
|
|
108
|
-
| 0.8. - 0.9.2 | React 0.13 |
|
|
109
|
-
| < 0.8 | React 0.12 |
|
|
169
|
+
| Version | Compatibility |
|
|
170
|
+
| --------- | --------------------- |
|
|
171
|
+
| >= 2.0.0 | React 18+, TypeScript |
|
|
172
|
+
| >= 0.17.0 | React 16 & 17 |
|
|
110
173
|
|
|
111
174
|
## Installation
|
|
112
175
|
|
|
113
|
-
Install the React-Grid-Layout [package](https://www.npmjs.org/package/react-grid-layout) using [npm](https://www.npmjs.com/):
|
|
114
|
-
|
|
115
176
|
```bash
|
|
116
177
|
npm install react-grid-layout
|
|
117
178
|
```
|
|
118
179
|
|
|
119
|
-
Include the
|
|
180
|
+
Include the stylesheets in your application:
|
|
120
181
|
|
|
182
|
+
```js
|
|
183
|
+
import "react-grid-layout/css/styles.css";
|
|
184
|
+
import "react-resizable/css/styles.css";
|
|
121
185
|
```
|
|
122
|
-
|
|
123
|
-
|
|
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" />
|
|
124
192
|
```
|
|
125
193
|
|
|
126
|
-
##
|
|
194
|
+
## Quick Start
|
|
127
195
|
|
|
128
|
-
|
|
129
|
-
|
|
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";
|
|
130
200
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- users will be able to freely drag and resize item `c`
|
|
201
|
+
function MyGrid() {
|
|
202
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
134
203
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<div key="c">c</div>
|
|
157
|
-
</GridLayout>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
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
|
+
);
|
|
160
225
|
}
|
|
161
226
|
```
|
|
162
227
|
|
|
163
|
-
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
|
+
```
|
|
164
243
|
|
|
165
|
-
|
|
166
|
-
import GridLayout from "react-grid-layout";
|
|
244
|
+
## Responsive Usage
|
|
167
245
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
+
);
|
|
184
275
|
}
|
|
185
276
|
```
|
|
186
277
|
|
|
187
|
-
|
|
278
|
+
## Providing Grid Width
|
|
188
279
|
|
|
189
|
-
|
|
190
|
-
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:
|
|
191
281
|
|
|
192
|
-
###
|
|
282
|
+
### Option 1: useContainerWidth Hook (Recommended)
|
|
193
283
|
|
|
194
|
-
|
|
284
|
+
```tsx
|
|
285
|
+
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
|
|
195
286
|
|
|
196
|
-
|
|
197
|
-
|
|
287
|
+
function MyGrid() {
|
|
288
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
198
289
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<ResponsiveGridLayout
|
|
205
|
-
className="layout"
|
|
206
|
-
layouts={layouts}
|
|
207
|
-
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
208
|
-
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
209
|
-
>
|
|
210
|
-
<div key="1">1</div>
|
|
211
|
-
<div key="2">2</div>
|
|
212
|
-
<div key="3">3</div>
|
|
213
|
-
</ResponsiveGridLayout>
|
|
214
|
-
);
|
|
215
|
-
}
|
|
290
|
+
return (
|
|
291
|
+
<div ref={containerRef}>
|
|
292
|
+
{mounted && <ReactGridLayout width={width}>...</ReactGridLayout>}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
216
295
|
}
|
|
217
296
|
```
|
|
218
297
|
|
|
219
|
-
|
|
298
|
+
### Option 2: Fixed Width
|
|
220
299
|
|
|
221
|
-
|
|
222
|
-
|
|
300
|
+
```tsx
|
|
301
|
+
<ReactGridLayout width={1200}>...</ReactGridLayout>
|
|
302
|
+
```
|
|
223
303
|
|
|
224
|
-
|
|
225
|
-
`WidthProvider` as per the instructions below.
|
|
304
|
+
### Option 3: CSS Container Queries or ResizeObserver
|
|
226
305
|
|
|
227
|
-
|
|
228
|
-
items, so that they would be taken into account within layout interpolation.
|
|
306
|
+
Use any width measurement library like [react-sizeme](https://github.com/ctrlplusb/react-sizeme) or your own ResizeObserver implementation.
|
|
229
307
|
|
|
230
|
-
###
|
|
308
|
+
### Option 4: Legacy WidthProvider HOC
|
|
231
309
|
|
|
232
|
-
|
|
233
|
-
positions on drag events. In simple cases a HOC `WidthProvider` can be used to automatically determine
|
|
234
|
-
width upon initialization and window resize events.
|
|
310
|
+
For backwards compatibility, you can still use `WidthProvider`:
|
|
235
311
|
|
|
236
|
-
```
|
|
237
|
-
import {
|
|
312
|
+
```tsx
|
|
313
|
+
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
|
|
238
314
|
|
|
239
|
-
const
|
|
315
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
240
316
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
// {lg: layout1, md: layout2, ...}
|
|
244
|
-
var layouts = getLayoutsFromSomewhere();
|
|
245
|
-
return (
|
|
246
|
-
<ResponsiveGridLayout
|
|
247
|
-
className="layout"
|
|
248
|
-
layouts={layouts}
|
|
249
|
-
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
250
|
-
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
251
|
-
>
|
|
252
|
-
<div key="1">1</div>
|
|
253
|
-
<div key="2">2</div>
|
|
254
|
-
<div key="3">3</div>
|
|
255
|
-
</ResponsiveGridLayout>
|
|
256
|
-
);
|
|
257
|
-
}
|
|
317
|
+
function MyGrid() {
|
|
318
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
258
319
|
}
|
|
259
320
|
```
|
|
260
321
|
|
|
261
|
-
|
|
322
|
+
## Hooks API
|
|
262
323
|
|
|
263
|
-
|
|
264
|
-
container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation
|
|
265
|
-
on application/component mount.
|
|
324
|
+
The v2 API provides three hooks for different use cases. Choose based on your needs:
|
|
266
325
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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 |
|
|
270
331
|
|
|
271
|
-
###
|
|
332
|
+
### useContainerWidth
|
|
272
333
|
|
|
273
|
-
|
|
334
|
+
Observes container width using ResizeObserver and provides reactive width updates. This is the recommended way to provide width to the grid.
|
|
274
335
|
|
|
275
|
-
|
|
276
|
-
//
|
|
277
|
-
// Basic props
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
// This allows setting the initial width on the server side.
|
|
281
|
-
// This is required unless using the HOC <WidthProvider> or similar
|
|
282
|
-
width: number,
|
|
283
|
-
|
|
284
|
-
// If true, the container height swells and contracts to fit contents
|
|
285
|
-
autoSize: ?boolean = true,
|
|
286
|
-
|
|
287
|
-
// Number of columns in this layout.
|
|
288
|
-
cols: ?number = 12,
|
|
289
|
-
|
|
290
|
-
// A CSS selector for tags that will not be draggable.
|
|
291
|
-
// For example: draggableCancel:'.MyNonDraggableAreaClassName'
|
|
292
|
-
// If you forget the leading . it will not work.
|
|
293
|
-
// .react-resizable-handle" is always prepended to this value.
|
|
294
|
-
draggableCancel: ?string = '',
|
|
295
|
-
|
|
296
|
-
// A CSS selector for tags that will act as the draggable handle.
|
|
297
|
-
// For example: draggableHandle:'.MyDragHandleClassName'
|
|
298
|
-
// If you forget the leading . it will not work.
|
|
299
|
-
draggableHandle: ?string = '',
|
|
300
|
-
|
|
301
|
-
// Compaction type.
|
|
302
|
-
compactType: ?('vertical' | 'horizontal' | null) = 'vertical';
|
|
303
|
-
|
|
304
|
-
// Layout is an array of objects with the format:
|
|
305
|
-
// The index into the layout must match the key used on each item component.
|
|
306
|
-
// If you choose to use custom keys, you can specify that key in the layout
|
|
307
|
-
// array objects using the `i` prop.
|
|
308
|
-
layout: ?Array<{i?: string, x: number, y: number, w: number, h: number}> = null, // If not provided, use data-grid props on children
|
|
309
|
-
|
|
310
|
-
// Margin between items [x, y] in px.
|
|
311
|
-
margin: ?[number, number] = [10, 10],
|
|
312
|
-
|
|
313
|
-
// Padding inside the container [x, y] in px
|
|
314
|
-
containerPadding: ?[number, number] = margin,
|
|
315
|
-
|
|
316
|
-
// Rows have a static height, but you can change this based on breakpoints
|
|
317
|
-
// if you like.
|
|
318
|
-
rowHeight: ?number = 150,
|
|
319
|
-
|
|
320
|
-
// Configuration of a dropping element. Dropping element is a "virtual" element
|
|
321
|
-
// which appears when you drag over some element from outside.
|
|
322
|
-
// It can be changed by passing specific parameters:
|
|
323
|
-
// i - id of an element
|
|
324
|
-
// w - width of an element
|
|
325
|
-
// h - height of an element
|
|
326
|
-
droppingItem?: { i: string, w: number, h: number }
|
|
327
|
-
|
|
328
|
-
//
|
|
329
|
-
// Flags
|
|
330
|
-
//
|
|
331
|
-
isDraggable: ?boolean = true,
|
|
332
|
-
isResizable: ?boolean = true,
|
|
333
|
-
isBounded: ?boolean = false,
|
|
334
|
-
// Uses CSS3 translate() instead of position top/left.
|
|
335
|
-
// This makes about 6x faster paint performance
|
|
336
|
-
useCSSTransforms: ?boolean = true,
|
|
337
|
-
// If parent DOM node of ResponsiveReactGridLayout or ReactGridLayout has "transform: scale(n)" css property,
|
|
338
|
-
// we should set scale coefficient to avoid render artefacts while dragging.
|
|
339
|
-
transformScale: ?number = 1,
|
|
340
|
-
|
|
341
|
-
// If true, grid can be placed one over the other.
|
|
342
|
-
// If set, implies `preventCollision`.
|
|
343
|
-
allowOverlap: ?boolean = false,
|
|
344
|
-
|
|
345
|
-
// If true, grid items won't change position when being
|
|
346
|
-
// dragged over. If `allowOverlap` is still false,
|
|
347
|
-
// this simply won't allow one to drop on an existing object.
|
|
348
|
-
preventCollision: ?boolean = false,
|
|
349
|
-
|
|
350
|
-
// If true, droppable elements (with `draggable={true}` attribute)
|
|
351
|
-
// can be dropped on the grid. It triggers "onDrop" callback
|
|
352
|
-
// with position and event object as parameters.
|
|
353
|
-
// It can be useful for dropping an element in a specific position
|
|
354
|
-
//
|
|
355
|
-
// NOTE: In case of using Firefox you should add
|
|
356
|
-
// `onDragStart={e => e.dataTransfer.setData('text/plain', '')}` attribute
|
|
357
|
-
// along with `draggable={true}` otherwise this feature will work incorrect.
|
|
358
|
-
// onDragStart attribute is required for Firefox for a dragging initialization
|
|
359
|
-
// @see https://bugzilla.mozilla.org/show_bug.cgi?id=568313
|
|
360
|
-
isDroppable: ?boolean = false,
|
|
361
|
-
// Defines which resize handles should be rendered.
|
|
362
|
-
// Allows for any combination of:
|
|
363
|
-
// 's' - South handle (bottom-center)
|
|
364
|
-
// 'w' - West handle (left-center)
|
|
365
|
-
// 'e' - East handle (right-center)
|
|
366
|
-
// 'n' - North handle (top-center)
|
|
367
|
-
// 'sw' - Southwest handle (bottom-left)
|
|
368
|
-
// 'nw' - Northwest handle (top-left)
|
|
369
|
-
// 'se' - Southeast handle (bottom-right)
|
|
370
|
-
// 'ne' - Northeast handle (top-right)
|
|
371
|
-
//
|
|
372
|
-
// Note that changing this property dynamically does not work due to a restriction in react-resizable.
|
|
373
|
-
resizeHandles: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se'],
|
|
374
|
-
// Custom component for resize handles
|
|
375
|
-
// See `handle` as used in https://github.com/react-grid-layout/react-resizable#resize-handle
|
|
376
|
-
// Your component should have the class `.react-resizable-handle`, or you should add your custom
|
|
377
|
-
// class to the `draggableCancel` prop.
|
|
378
|
-
resizeHandle?: ReactElement<any> | ((resizeHandleAxis: ResizeHandleAxis, ref: ReactRef<HTMLElement>) => ReactElement<any>),
|
|
379
|
-
|
|
380
|
-
//
|
|
381
|
-
// Callbacks
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
// Callback so you can save the layout.
|
|
385
|
-
// Calls back with (currentLayout) after every drag or resize stop.
|
|
386
|
-
onLayoutChange: (layout: Layout) => void,
|
|
387
|
-
|
|
388
|
-
//
|
|
389
|
-
// All callbacks below have signature (layout, oldItem, newItem, placeholder, e, element).
|
|
390
|
-
// 'start' and 'stop' callbacks pass `undefined` for 'placeholder'.
|
|
391
|
-
//
|
|
392
|
-
type ItemCallback = (layout: Layout, oldItem: LayoutItem, newItem: LayoutItem,
|
|
393
|
-
placeholder: LayoutItem, e: MouseEvent, element: HTMLElement) => void,
|
|
394
|
-
|
|
395
|
-
// Calls when drag starts.
|
|
396
|
-
onDragStart: ItemCallback,
|
|
397
|
-
// Calls on each drag movement.
|
|
398
|
-
onDrag: ItemCallback,
|
|
399
|
-
// Calls when drag is complete.
|
|
400
|
-
onDragStop: ItemCallback,
|
|
401
|
-
// Calls when resize starts.
|
|
402
|
-
onResizeStart: ItemCallback,
|
|
403
|
-
// Calls when resize movement happens.
|
|
404
|
-
onResize: ItemCallback,
|
|
405
|
-
// Calls when resize is complete.
|
|
406
|
-
onResizeStop: ItemCallback,
|
|
407
|
-
|
|
408
|
-
//
|
|
409
|
-
// Dropover functionality
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
// Calls when an element has been dropped into the grid from outside.
|
|
413
|
-
onDrop: (layout: Layout, item: ?LayoutItem, e: Event) => void,
|
|
414
|
-
// Calls when an element is being dragged over the grid from outside as above.
|
|
415
|
-
// This callback should return an object to dynamically change the droppingItem size
|
|
416
|
-
// Return false to short-circuit the dragover
|
|
417
|
-
onDropDragOver: (e: DragOverEvent) => ?({|w?: number, h?: number|} | false),
|
|
418
|
-
|
|
419
|
-
// Ref for getting a reference for the grid's wrapping div.
|
|
420
|
-
// You can use this instead of a regular ref and the deprecated `ReactDOM.findDOMNode()`` function.
|
|
421
|
-
// Note that this type is React.Ref<HTMLDivElement> in TypeScript, Flow has a bug here
|
|
422
|
-
// https://github.com/facebook/flow/issues/8671#issuecomment-862634865
|
|
423
|
-
innerRef: {current: null | HTMLDivElement},
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
### Responsive Grid Layout Props
|
|
427
|
-
|
|
428
|
-
The responsive grid layout can be used instead. It supports all of the props above, excepting `layout`.
|
|
429
|
-
The new properties and changes are:
|
|
336
|
+
**Why use it instead of WidthProvider?**
|
|
430
337
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
|
435
342
|
|
|
436
|
-
|
|
437
|
-
|
|
343
|
+
```tsx
|
|
344
|
+
import { useContainerWidth } from "react-grid-layout";
|
|
438
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
|
+
});
|
|
439
351
|
|
|
440
|
-
|
|
441
|
-
|
|
352
|
+
return (
|
|
353
|
+
<div ref={containerRef}>{mounted && <ReactGridLayout width={width} />}</div>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
442
357
|
|
|
358
|
+
**Type Definitions:**
|
|
443
359
|
|
|
444
|
-
|
|
445
|
-
|
|
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
|
+
}
|
|
446
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
|
+
```
|
|
447
379
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
|
424
|
+
}
|
|
451
425
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
+
);
|
|
439
|
+
}
|
|
440
|
+
```
|
|
455
441
|
|
|
456
|
-
|
|
457
|
-
|
|
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 */
|
|
453
|
+
allowOverlap?: boolean;
|
|
454
|
+
/** Prevent collisions when moving items */
|
|
455
|
+
preventCollision?: boolean;
|
|
456
|
+
/** Called when layout changes */
|
|
457
|
+
onLayoutChange?: (layout: Layout) => void;
|
|
458
|
+
}
|
|
458
459
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
+
```
|
|
462
506
|
|
|
463
|
-
|
|
464
|
-
|
|
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
|
+
```
|
|
561
|
+
|
|
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
|
+
}
|
|
588
|
+
|
|
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
|
+
}
|
|
465
605
|
|
|
606
|
+
type DefaultBreakpoints = "lg" | "md" | "sm" | "xs" | "xxs";
|
|
466
607
|
```
|
|
467
608
|
|
|
468
|
-
|
|
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
|
+
```
|
|
651
|
+
|
|
652
|
+
### GridConfig
|
|
469
653
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
654
|
+
Grid measurement configuration:
|
|
655
|
+
|
|
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
|
+
```
|
|
473
665
|
|
|
474
|
-
|
|
666
|
+
### DragConfig
|
|
475
667
|
|
|
476
|
-
|
|
477
|
-
will be thrown so you can correct your layout.
|
|
668
|
+
Drag behavior configuration:
|
|
478
669
|
|
|
479
|
-
|
|
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
|
+
```
|
|
480
679
|
|
|
481
|
-
|
|
482
|
-
is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions
|
|
483
|
-
are out of range.
|
|
680
|
+
### ResizeConfig
|
|
484
681
|
|
|
485
|
-
|
|
486
|
-
example, if the layout has the property `isDraggable: false`, but the grid item has the prop `isDraggable: true`, the item
|
|
487
|
-
will be draggable, even if the item is marked `static: true`.
|
|
682
|
+
Resize behavior configuration:
|
|
488
683
|
|
|
489
|
-
```
|
|
490
|
-
{
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// These are all in grid units, not pixels
|
|
496
|
-
x: number,
|
|
497
|
-
y: number,
|
|
498
|
-
w: number,
|
|
499
|
-
h: number,
|
|
500
|
-
minW: ?number = 0,
|
|
501
|
-
maxW: ?number = Infinity,
|
|
502
|
-
minH: ?number = 0,
|
|
503
|
-
maxH: ?number = Infinity,
|
|
504
|
-
|
|
505
|
-
// If true, equal to `isDraggable: false, isResizable: false`.
|
|
506
|
-
static: ?boolean = false,
|
|
507
|
-
// If false, will not be draggable. Overrides `static`.
|
|
508
|
-
isDraggable: ?boolean = true,
|
|
509
|
-
// If false, will not be resizable. Overrides `static`.
|
|
510
|
-
isResizable: ?boolean = true,
|
|
511
|
-
// By default, a handle is only shown on the bottom-right (southeast) corner.
|
|
512
|
-
// As of RGL >= 1.4.0, resizing on any corner works just fine!
|
|
513
|
-
resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
|
|
514
|
-
// If true and draggable, item will be moved only within grid.
|
|
515
|
-
isBounded: ?boolean = false
|
|
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);
|
|
516
689
|
}
|
|
517
690
|
```
|
|
518
691
|
|
|
519
|
-
###
|
|
692
|
+
### DropConfig
|
|
520
693
|
|
|
521
|
-
|
|
694
|
+
External drop configuration:
|
|
522
695
|
|
|
523
|
-
|
|
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
|
+
}
|
|
702
|
+
```
|
|
524
703
|
|
|
525
|
-
|
|
704
|
+
### PositionStrategy
|
|
526
705
|
|
|
527
|
-
|
|
706
|
+
CSS positioning strategy. Built-in options:
|
|
528
707
|
|
|
529
|
-
|
|
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";
|
|
530
714
|
|
|
531
|
-
|
|
715
|
+
// Example: scaled container
|
|
716
|
+
<div style={{ transform: 'scale(0.5)' }}>
|
|
717
|
+
<ReactGridLayout positionStrategy={createScaledStrategy(0.5)} ... />
|
|
718
|
+
</div>
|
|
719
|
+
```
|
|
532
720
|
|
|
533
|
-
###
|
|
721
|
+
### Compactor
|
|
534
722
|
|
|
535
|
-
|
|
723
|
+
Layout compaction strategy. Built-in options:
|
|
536
724
|
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
//
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
+
```
|
|
733
|
+
|
|
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;
|
|
549
762
|
}
|
|
550
|
-
// ...
|
|
551
763
|
```
|
|
552
764
|
|
|
553
|
-
|
|
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
|
+
```
|
|
554
785
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
786
|
+
### Core Utilities
|
|
787
|
+
|
|
788
|
+
Import pure layout functions from `react-grid-layout/core`:
|
|
789
|
+
|
|
790
|
+
```ts
|
|
791
|
+
import {
|
|
792
|
+
compact,
|
|
793
|
+
moveElement,
|
|
794
|
+
collides,
|
|
795
|
+
getFirstCollision,
|
|
796
|
+
validateLayout
|
|
797
|
+
// ... and more
|
|
798
|
+
} from "react-grid-layout/core";
|
|
799
|
+
```
|
|
800
|
+
|
|
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
|
+
/** Whether this compactor allows overlapping items */
|
|
815
|
+
allowOverlap: boolean;
|
|
816
|
+
|
|
817
|
+
/** Prevent items from moving when another item is dragged into them */
|
|
818
|
+
preventCollision?: boolean;
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Compact the entire layout.
|
|
822
|
+
* Called after any layout change to fill gaps.
|
|
823
|
+
*
|
|
824
|
+
* @param layout - Array of layout items (clone before mutating!)
|
|
825
|
+
* @param cols - Number of grid columns
|
|
826
|
+
* @returns New compacted layout
|
|
827
|
+
*/
|
|
828
|
+
compact(layout: Layout, cols: number): Layout;
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Handle moving an item.
|
|
832
|
+
* Called during drag to preview the new position.
|
|
833
|
+
*
|
|
834
|
+
* @param layout - Current layout
|
|
835
|
+
* @param item - Item being moved
|
|
836
|
+
* @param x - New X position in grid units
|
|
837
|
+
* @param y - New Y position in grid units
|
|
838
|
+
* @param cols - Number of grid columns
|
|
839
|
+
* @returns Updated layout with item at new position
|
|
840
|
+
*/
|
|
841
|
+
onMove(
|
|
842
|
+
layout: Layout,
|
|
843
|
+
item: LayoutItem,
|
|
844
|
+
x: number,
|
|
845
|
+
y: number,
|
|
846
|
+
cols: number
|
|
847
|
+
): Layout;
|
|
563
848
|
}
|
|
564
849
|
```
|
|
565
850
|
|
|
566
|
-
|
|
851
|
+
**Example: Gravity Compactor (items fall to bottom)**
|
|
852
|
+
|
|
853
|
+
```ts
|
|
854
|
+
import { cloneLayout, cloneLayoutItem, getStatics, bottom } from "react-grid-layout/core";
|
|
855
|
+
|
|
856
|
+
const gravityCompactor: Compactor = {
|
|
857
|
+
type: "gravity",
|
|
858
|
+
allowOverlap: false,
|
|
859
|
+
|
|
860
|
+
compact(layout, cols) {
|
|
861
|
+
const statics = getStatics(layout);
|
|
862
|
+
const maxY = 100; // arbitrary max height
|
|
863
|
+
const out = [];
|
|
864
|
+
|
|
865
|
+
// Sort by Y descending (process bottom items first)
|
|
866
|
+
const sorted = [...layout].sort((a, b) => b.y - a.y);
|
|
867
|
+
|
|
868
|
+
for (const item of sorted) {
|
|
869
|
+
const l = cloneLayoutItem(item);
|
|
870
|
+
|
|
871
|
+
if (!l.static) {
|
|
872
|
+
// Move down as far as possible
|
|
873
|
+
while (l.y < maxY && !collides(l, statics)) {
|
|
874
|
+
l.y++;
|
|
875
|
+
}
|
|
876
|
+
l.y--; // Back up one
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
out.push(l);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return out;
|
|
883
|
+
},
|
|
884
|
+
|
|
885
|
+
onMove(layout, item, x, y, cols) {
|
|
886
|
+
const newLayout = cloneLayout(layout);
|
|
887
|
+
const movedItem = newLayout.find(l => l.i === item.i);
|
|
888
|
+
if (movedItem) {
|
|
889
|
+
movedItem.x = x;
|
|
890
|
+
movedItem.y = y;
|
|
891
|
+
movedItem.moved = true;
|
|
892
|
+
}
|
|
893
|
+
return newLayout;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
567
896
|
|
|
568
|
-
|
|
897
|
+
// Usage
|
|
898
|
+
<GridLayout compactor={gravityCompactor} />
|
|
899
|
+
```
|
|
569
900
|
|
|
570
|
-
|
|
571
|
-
|
|
901
|
+
**Example: Single Row Compactor (horizontal shelf)**
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
const singleRowCompactor: Compactor = {
|
|
905
|
+
type: "shelf",
|
|
906
|
+
allowOverlap: false,
|
|
907
|
+
|
|
908
|
+
compact(layout, cols) {
|
|
909
|
+
let x = 0;
|
|
910
|
+
const out = [];
|
|
911
|
+
|
|
912
|
+
// Sort by original X position
|
|
913
|
+
const sorted = [...layout].sort((a, b) => a.x - b.x);
|
|
914
|
+
|
|
915
|
+
for (const item of sorted) {
|
|
916
|
+
const l = cloneLayoutItem(item);
|
|
917
|
+
if (!l.static) {
|
|
918
|
+
l.x = x;
|
|
919
|
+
l.y = 0; // All items on row 0
|
|
920
|
+
x += l.w;
|
|
921
|
+
|
|
922
|
+
// Wrap to next row if overflow
|
|
923
|
+
if (x > cols) {
|
|
924
|
+
l.x = 0;
|
|
925
|
+
x = l.w;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
out.push(l);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return out;
|
|
932
|
+
},
|
|
933
|
+
|
|
934
|
+
onMove(layout, item, x, y, cols) {
|
|
935
|
+
// Same as default - just update position
|
|
936
|
+
const newLayout = cloneLayout(layout);
|
|
937
|
+
const movedItem = newLayout.find(l => l.i === item.i);
|
|
938
|
+
if (movedItem) {
|
|
939
|
+
movedItem.x = x;
|
|
940
|
+
movedItem.y = 0; // Force row 0
|
|
941
|
+
movedItem.moved = true;
|
|
942
|
+
}
|
|
943
|
+
return newLayout;
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
```
|
|
572
947
|
|
|
573
|
-
|
|
574
|
-
|
|
948
|
+
**Using Helper Functions:**
|
|
949
|
+
|
|
950
|
+
The core module exports helpers for building compactors:
|
|
951
|
+
|
|
952
|
+
```ts
|
|
953
|
+
import {
|
|
954
|
+
resolveCompactionCollision, // Move items to resolve overlaps
|
|
955
|
+
compactItemVertical, // Compact one item upward
|
|
956
|
+
compactItemHorizontal, // Compact one item leftward
|
|
957
|
+
getFirstCollision, // Find first collision
|
|
958
|
+
collides, // Check if two items collide
|
|
959
|
+
getStatics, // Get static items from layout
|
|
960
|
+
cloneLayout, // Clone layout array
|
|
961
|
+
cloneLayoutItem // Clone single item
|
|
962
|
+
} from "react-grid-layout/core";
|
|
575
963
|
```
|
|
576
964
|
|
|
577
|
-
###
|
|
965
|
+
### Creating a Custom Position Strategy
|
|
966
|
+
|
|
967
|
+
Position strategies control how items are positioned via CSS. Create custom strategies for special transform handling.
|
|
968
|
+
|
|
969
|
+
**The PositionStrategy Interface:**
|
|
970
|
+
|
|
971
|
+
```ts
|
|
972
|
+
interface PositionStrategy {
|
|
973
|
+
/** Type identifier */
|
|
974
|
+
type: "transform" | "absolute" | string;
|
|
975
|
+
|
|
976
|
+
/** Scale factor for coordinate calculations */
|
|
977
|
+
scale: number;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Generate CSS styles for positioning an item.
|
|
981
|
+
*
|
|
982
|
+
* @param pos - Position with top, left, width, height in pixels
|
|
983
|
+
* @returns CSS properties object
|
|
984
|
+
*/
|
|
985
|
+
calcStyle(pos: Position): React.CSSProperties;
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Calculate drag position from mouse coordinates.
|
|
989
|
+
* Used during drag to convert screen coords to grid coords.
|
|
990
|
+
*
|
|
991
|
+
* @param clientX - Mouse X position
|
|
992
|
+
* @param clientY - Mouse Y position
|
|
993
|
+
* @param offsetX - Offset from item left edge
|
|
994
|
+
* @param offsetY - Offset from item top edge
|
|
995
|
+
* @returns Calculated left/top position
|
|
996
|
+
*/
|
|
997
|
+
calcDragPosition(
|
|
998
|
+
clientX: number,
|
|
999
|
+
clientY: number,
|
|
1000
|
+
offsetX: number,
|
|
1001
|
+
offsetY: number
|
|
1002
|
+
): { left: number; top: number };
|
|
1003
|
+
}
|
|
1004
|
+
```
|
|
578
1005
|
|
|
579
|
-
|
|
1006
|
+
**Example: Rotated Container Strategy**
|
|
1007
|
+
|
|
1008
|
+
```ts
|
|
1009
|
+
const createRotatedStrategy = (angleDegrees: number): PositionStrategy => {
|
|
1010
|
+
const angleRad = (angleDegrees * Math.PI) / 180;
|
|
1011
|
+
const cos = Math.cos(angleRad);
|
|
1012
|
+
const sin = Math.sin(angleRad);
|
|
1013
|
+
|
|
1014
|
+
return {
|
|
1015
|
+
type: "rotated",
|
|
1016
|
+
scale: 1,
|
|
1017
|
+
|
|
1018
|
+
calcStyle(pos) {
|
|
1019
|
+
// Apply rotation to position
|
|
1020
|
+
const rotatedX = pos.left * cos - pos.top * sin;
|
|
1021
|
+
const rotatedY = pos.left * sin + pos.top * cos;
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
transform: `translate(${rotatedX}px, ${rotatedY}px)`,
|
|
1025
|
+
width: `${pos.width}px`,
|
|
1026
|
+
height: `${pos.height}px`,
|
|
1027
|
+
position: "absolute"
|
|
1028
|
+
};
|
|
1029
|
+
},
|
|
1030
|
+
|
|
1031
|
+
calcDragPosition(clientX, clientY, offsetX, offsetY) {
|
|
1032
|
+
// Reverse the rotation for drag calculations
|
|
1033
|
+
const x = clientX - offsetX;
|
|
1034
|
+
const y = clientY - offsetY;
|
|
1035
|
+
|
|
1036
|
+
return {
|
|
1037
|
+
left: x * cos + y * sin,
|
|
1038
|
+
top: -x * sin + y * cos
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
// Usage: grid inside a rotated container
|
|
1045
|
+
<div style={{ transform: 'rotate(45deg)' }}>
|
|
1046
|
+
<GridLayout positionStrategy={createRotatedStrategy(45)} />
|
|
1047
|
+
</div>
|
|
1048
|
+
```
|
|
580
1049
|
|
|
581
|
-
|
|
582
|
-
|
|
1050
|
+
**Example: 3D Perspective Strategy**
|
|
1051
|
+
|
|
1052
|
+
```ts
|
|
1053
|
+
const create3DStrategy = (
|
|
1054
|
+
perspective: number,
|
|
1055
|
+
rotateX: number
|
|
1056
|
+
): PositionStrategy => ({
|
|
1057
|
+
type: "3d",
|
|
1058
|
+
scale: 1,
|
|
1059
|
+
|
|
1060
|
+
calcStyle(pos) {
|
|
1061
|
+
return {
|
|
1062
|
+
transform: `
|
|
1063
|
+
perspective(${perspective}px)
|
|
1064
|
+
rotateX(${rotateX}deg)
|
|
1065
|
+
translate3d(${pos.left}px, ${pos.top}px, 0)
|
|
1066
|
+
`,
|
|
1067
|
+
width: `${pos.width}px`,
|
|
1068
|
+
height: `${pos.height}px`,
|
|
1069
|
+
position: "absolute",
|
|
1070
|
+
transformStyle: "preserve-3d"
|
|
1071
|
+
};
|
|
1072
|
+
},
|
|
1073
|
+
|
|
1074
|
+
calcDragPosition(clientX, clientY, offsetX, offsetY) {
|
|
1075
|
+
// Adjust for perspective foreshortening
|
|
1076
|
+
const perspectiveFactor = 1 + clientY / perspective;
|
|
1077
|
+
return {
|
|
1078
|
+
left: (clientX - offsetX) / perspectiveFactor,
|
|
1079
|
+
top: (clientY - offsetY) / perspectiveFactor
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
```
|
|
583
1084
|
|
|
584
|
-
|
|
1085
|
+
## Extras
|
|
1086
|
+
|
|
1087
|
+
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.
|
|
1088
|
+
|
|
1089
|
+
### GridBackground
|
|
1090
|
+
|
|
1091
|
+
Renders an SVG grid background that aligns with GridLayout cells. Use this to visualize the grid structure behind your layout.
|
|
1092
|
+
|
|
1093
|
+
> Based on [PR #2175](https://github.com/react-grid-layout/react-grid-layout/pull/2175) by [@nicosayer](https://github.com/nicosayer).
|
|
1094
|
+
|
|
1095
|
+
```tsx
|
|
1096
|
+
import { GridBackground } from "react-grid-layout/extras";
|
|
1097
|
+
import ReactGridLayout, { useContainerWidth } from "react-grid-layout";
|
|
1098
|
+
|
|
1099
|
+
function MyGrid() {
|
|
1100
|
+
const { width, containerRef, mounted } = useContainerWidth();
|
|
585
1101
|
|
|
586
|
-
```js
|
|
587
|
-
const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
|
|
588
1102
|
return (
|
|
589
|
-
<div
|
|
590
|
-
{
|
|
591
|
-
|
|
1103
|
+
<div ref={containerRef} style={{ position: "relative" }}>
|
|
1104
|
+
{mounted && (
|
|
1105
|
+
<>
|
|
1106
|
+
<GridBackground
|
|
1107
|
+
width={width}
|
|
1108
|
+
cols={12}
|
|
1109
|
+
rowHeight={30}
|
|
1110
|
+
margin={[10, 10]}
|
|
1111
|
+
rows={10}
|
|
1112
|
+
color="#f0f0f0"
|
|
1113
|
+
borderRadius={4}
|
|
1114
|
+
/>
|
|
1115
|
+
<ReactGridLayout
|
|
1116
|
+
width={width}
|
|
1117
|
+
gridConfig={{ cols: 12, rowHeight: 30, margin: [10, 10] }}
|
|
1118
|
+
>
|
|
1119
|
+
{children}
|
|
1120
|
+
</ReactGridLayout>
|
|
1121
|
+
</>
|
|
1122
|
+
)}
|
|
592
1123
|
</div>
|
|
593
1124
|
);
|
|
594
|
-
}
|
|
1125
|
+
}
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
**Props:**
|
|
1129
|
+
|
|
1130
|
+
```ts
|
|
1131
|
+
interface GridBackgroundProps {
|
|
1132
|
+
// Required - must match your GridLayout config
|
|
1133
|
+
width: number; // Container width
|
|
1134
|
+
cols: number; // Number of columns
|
|
1135
|
+
rowHeight: number; // Row height in pixels
|
|
1136
|
+
|
|
1137
|
+
// Optional
|
|
1138
|
+
margin?: [number, number]; // Gap between cells (default: [10, 10])
|
|
1139
|
+
containerPadding?: [number, number] | null; // Container padding (default: uses margin)
|
|
1140
|
+
rows?: number | "auto"; // Number of rows to display (default: 10)
|
|
1141
|
+
height?: number; // Used when rows="auto" to calculate row count
|
|
1142
|
+
color?: string; // Cell background color (default: "#e0e0e0")
|
|
1143
|
+
borderRadius?: number; // Cell border radius (default: 4)
|
|
1144
|
+
className?: string; // Additional CSS class
|
|
1145
|
+
style?: React.CSSProperties; // Additional inline styles
|
|
1146
|
+
}
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
### calcGridCellDimensions (Core Utility)
|
|
1150
|
+
|
|
1151
|
+
For building custom grid overlays or backgrounds, use the `calcGridCellDimensions` utility from `react-grid-layout/core`:
|
|
1152
|
+
|
|
1153
|
+
```ts
|
|
1154
|
+
import { calcGridCellDimensions } from "react-grid-layout/core";
|
|
1155
|
+
|
|
1156
|
+
const dims = calcGridCellDimensions({
|
|
1157
|
+
width: 1200,
|
|
1158
|
+
cols: 12,
|
|
1159
|
+
rowHeight: 30,
|
|
1160
|
+
margin: [10, 10],
|
|
1161
|
+
containerPadding: [20, 20]
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
// dims = {
|
|
1165
|
+
// cellWidth: 88.33, // Width of each cell
|
|
1166
|
+
// cellHeight: 30, // Height of each cell (= rowHeight)
|
|
1167
|
+
// offsetX: 20, // Left padding
|
|
1168
|
+
// offsetY: 20, // Top padding
|
|
1169
|
+
// gapX: 10, // Horizontal gap between cells
|
|
1170
|
+
// gapY: 10, // Vertical gap between cells
|
|
1171
|
+
// cols: 12, // Column count
|
|
1172
|
+
// containerWidth: 1200
|
|
1173
|
+
// }
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
This is useful for building custom visualizations, snap-to-grid functionality, or integrating with canvas/WebGL renderers.
|
|
1177
|
+
|
|
1178
|
+
## Performance
|
|
1179
|
+
|
|
1180
|
+
### Memoize Children
|
|
1181
|
+
|
|
1182
|
+
The grid compares children by reference. Memoize them for better performance:
|
|
1183
|
+
|
|
1184
|
+
```tsx
|
|
1185
|
+
function MyGrid({ count, width }) {
|
|
1186
|
+
const children = useMemo(() => {
|
|
1187
|
+
return Array.from({ length: count }, (_, i) => (
|
|
1188
|
+
<div
|
|
1189
|
+
key={i}
|
|
1190
|
+
data-grid={{ x: i % 12, y: Math.floor(i / 12), w: 1, h: 1 }}
|
|
1191
|
+
/>
|
|
1192
|
+
));
|
|
1193
|
+
}, [count]);
|
|
1194
|
+
|
|
1195
|
+
return (
|
|
1196
|
+
<ReactGridLayout width={width} gridConfig={{ cols: 12 }}>
|
|
1197
|
+
{children}
|
|
1198
|
+
</ReactGridLayout>
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
595
1201
|
```
|
|
596
1202
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
1203
|
+
### Avoid Creating Components in Render (Legacy WidthProvider)
|
|
1204
|
+
|
|
1205
|
+
If using the legacy WidthProvider HOC, don't create the component during render:
|
|
1206
|
+
|
|
1207
|
+
```tsx
|
|
1208
|
+
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
|
|
1209
|
+
|
|
1210
|
+
// Bad - creates new component every render
|
|
1211
|
+
function MyGrid() {
|
|
1212
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
1213
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// Good - create once outside or with useMemo
|
|
1217
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
1218
|
+
|
|
1219
|
+
function MyGrid() {
|
|
1220
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
1221
|
+
}
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
With the v2 API, use `useContainerWidth` hook instead to avoid this issue entirely.
|
|
1225
|
+
|
|
1226
|
+
## Custom Child Components
|
|
1227
|
+
|
|
1228
|
+
Grid children must forward refs and certain props:
|
|
1229
|
+
|
|
1230
|
+
```tsx
|
|
1231
|
+
const CustomItem = forwardRef<HTMLDivElement, CustomItemProps>(
|
|
1232
|
+
(
|
|
1233
|
+
{
|
|
1234
|
+
style,
|
|
1235
|
+
className,
|
|
1236
|
+
onMouseDown,
|
|
1237
|
+
onMouseUp,
|
|
1238
|
+
onTouchEnd,
|
|
1239
|
+
children,
|
|
1240
|
+
...props
|
|
1241
|
+
},
|
|
1242
|
+
ref
|
|
1243
|
+
) => {
|
|
1244
|
+
return (
|
|
1245
|
+
<div
|
|
1246
|
+
ref={ref}
|
|
1247
|
+
style={style}
|
|
1248
|
+
className={className}
|
|
1249
|
+
onMouseDown={onMouseDown}
|
|
1250
|
+
onMouseUp={onMouseUp}
|
|
1251
|
+
onTouchEnd={onTouchEnd}
|
|
1252
|
+
>
|
|
1253
|
+
{children}
|
|
1254
|
+
</div>
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
```
|
|
600
1259
|
|
|
601
1260
|
## Contribute
|
|
602
1261
|
|
|
@@ -604,19 +1263,3 @@ If you have a feature request, please add it as an issue or make a pull request.
|
|
|
604
1263
|
|
|
605
1264
|
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
|
|
606
1265
|
us easily isolate it.
|
|
607
|
-
|
|
608
|
-
## TODO List
|
|
609
|
-
|
|
610
|
-
- [x] Basic grid layout
|
|
611
|
-
- [x] Fluid grid layout
|
|
612
|
-
- [x] Grid packing
|
|
613
|
-
- [x] Draggable grid items
|
|
614
|
-
- [x] Live grid packing while dragging
|
|
615
|
-
- [x] Resizable grid items
|
|
616
|
-
- [x] Layouts per responsive breakpoint
|
|
617
|
-
- [x] Define grid attributes on children themselves (`data-grid` key)
|
|
618
|
-
- [x] Static elements
|
|
619
|
-
- [x] Persistent id per item for predictable localstorage restores, even when # items changes
|
|
620
|
-
- [x] Min/max w/h per item
|
|
621
|
-
- [x] Resizable handles on other corners
|
|
622
|
-
- [ ] Configurable w/h per breakpoint
|