react-grid-layout 1.5.3 → 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 +1074 -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 -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
|
+
| [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
|
|
|
@@ -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,1098 @@ _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
|
|
221
299
|
|
|
222
|
-
|
|
223
|
-
|
|
300
|
+
```tsx
|
|
301
|
+
<ReactGridLayout width={1200}>...</ReactGridLayout>
|
|
302
|
+
```
|
|
224
303
|
|
|
225
|
-
|
|
226
|
-
`WidthProvider` as per the instructions below.
|
|
304
|
+
### Option 3: CSS Container Queries or ResizeObserver
|
|
227
305
|
|
|
228
|
-
|
|
229
|
-
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.
|
|
230
307
|
|
|
231
|
-
###
|
|
308
|
+
### Option 4: Legacy WidthProvider HOC
|
|
232
309
|
|
|
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.
|
|
310
|
+
For backwards compatibility, you can still use `WidthProvider`:
|
|
236
311
|
|
|
237
|
-
```
|
|
238
|
-
import {
|
|
312
|
+
```tsx
|
|
313
|
+
import ReactGridLayout, { WidthProvider } from "react-grid-layout/legacy";
|
|
239
314
|
|
|
240
|
-
const
|
|
315
|
+
const GridLayoutWithWidth = WidthProvider(ReactGridLayout);
|
|
241
316
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// {lg: layout1, md: layout2, ...}
|
|
245
|
-
var layouts = getLayoutsFromSomewhere();
|
|
246
|
-
return (
|
|
247
|
-
<ResponsiveGridLayout
|
|
248
|
-
className="layout"
|
|
249
|
-
layouts={layouts}
|
|
250
|
-
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
|
251
|
-
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
|
252
|
-
>
|
|
253
|
-
<div key="1">1</div>
|
|
254
|
-
<div key="2">2</div>
|
|
255
|
-
<div key="3">3</div>
|
|
256
|
-
</ResponsiveGridLayout>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
317
|
+
function MyGrid() {
|
|
318
|
+
return <GridLayoutWithWidth>...</GridLayoutWithWidth>;
|
|
259
319
|
}
|
|
260
320
|
```
|
|
261
321
|
|
|
262
|
-
|
|
322
|
+
## Hooks API
|
|
263
323
|
|
|
264
|
-
|
|
265
|
-
container's width before mounting children. Use this if you'd like to completely eliminate any resizing animation
|
|
266
|
-
on application/component mount.
|
|
324
|
+
The v2 API provides three hooks for different use cases. Choose based on your needs:
|
|
267
325
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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 |
|
|
271
331
|
|
|
272
|
-
###
|
|
332
|
+
### useContainerWidth
|
|
273
333
|
|
|
274
|
-
|
|
334
|
+
Observes container width using ResizeObserver and provides reactive width updates. This is the recommended way to provide width to the grid.
|
|
275
335
|
|
|
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:
|
|
336
|
+
**Why use it instead of WidthProvider?**
|
|
431
337
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
436
342
|
|
|
437
|
-
|
|
438
|
-
|
|
343
|
+
```tsx
|
|
344
|
+
import { useContainerWidth } from "react-grid-layout";
|
|
439
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
|
+
});
|
|
440
351
|
|
|
441
|
-
|
|
442
|
-
|
|
352
|
+
return (
|
|
353
|
+
<div ref={containerRef}>{mounted && <ReactGridLayout width={width} />}</div>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
443
357
|
|
|
358
|
+
**Type Definitions:**
|
|
444
359
|
|
|
445
|
-
|
|
446
|
-
|
|
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
|
+
}
|
|
447
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
|
+
```
|
|
448
379
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
+
}
|
|
452
425
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
+
```
|
|
456
441
|
|
|
457
|
-
|
|
458
|
-
|
|
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
|
+
}
|
|
459
459
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
+
```
|
|
463
506
|
|
|
464
|
-
|
|
465
|
-
|
|
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
|
+
}
|
|
466
605
|
|
|
606
|
+
type DefaultBreakpoints = "lg" | "md" | "sm" | "xs" | "xxs";
|
|
467
607
|
```
|
|
468
608
|
|
|
469
|
-
|
|
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
|
|
470
653
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
+
```
|
|
474
665
|
|
|
475
|
-
|
|
666
|
+
### DragConfig
|
|
476
667
|
|
|
477
|
-
|
|
478
|
-
will be thrown so you can correct your layout.
|
|
668
|
+
Drag behavior configuration:
|
|
479
669
|
|
|
480
|
-
|
|
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
|
+
```
|
|
481
679
|
|
|
482
|
-
|
|
483
|
-
is disabled. Errors will be thrown if your mins and maxes overlap incorrectly, or your initial dimensions
|
|
484
|
-
are out of range.
|
|
680
|
+
### ResizeConfig
|
|
485
681
|
|
|
486
|
-
|
|
487
|
-
example, if the layout has the property `isDraggable: false`, but the grid item has the prop `isDraggable: true`, the item
|
|
488
|
-
will be draggable, even if the item is marked `static: true`.
|
|
682
|
+
Resize behavior configuration:
|
|
489
683
|
|
|
490
|
-
```
|
|
491
|
-
{
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
// These are all in grid units, not pixels
|
|
497
|
-
x: number,
|
|
498
|
-
y: number,
|
|
499
|
-
w: number,
|
|
500
|
-
h: number,
|
|
501
|
-
minW: ?number = 0,
|
|
502
|
-
maxW: ?number = Infinity,
|
|
503
|
-
minH: ?number = 0,
|
|
504
|
-
maxH: ?number = Infinity,
|
|
505
|
-
|
|
506
|
-
// If true, equal to `isDraggable: false, isResizable: false`.
|
|
507
|
-
static: ?boolean = false,
|
|
508
|
-
// If false, will not be draggable. Overrides `static`.
|
|
509
|
-
isDraggable: ?boolean = true,
|
|
510
|
-
// If false, will not be resizable. Overrides `static`.
|
|
511
|
-
isResizable: ?boolean = true,
|
|
512
|
-
// By default, a handle is only shown on the bottom-right (southeast) corner.
|
|
513
|
-
// As of RGL >= 1.4.0, resizing on any corner works just fine!
|
|
514
|
-
resizeHandles?: ?Array<'s' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne'> = ['se']
|
|
515
|
-
// If true and draggable, item will be moved only within grid.
|
|
516
|
-
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);
|
|
517
689
|
}
|
|
518
690
|
```
|
|
519
691
|
|
|
520
|
-
###
|
|
692
|
+
### DropConfig
|
|
521
693
|
|
|
522
|
-
|
|
694
|
+
External drop configuration:
|
|
523
695
|
|
|
524
|
-
|
|
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
|
+
```
|
|
525
703
|
|
|
526
|
-
|
|
704
|
+
### PositionStrategy
|
|
527
705
|
|
|
528
|
-
|
|
706
|
+
CSS positioning strategy. Built-in options:
|
|
529
707
|
|
|
530
|
-
|
|
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";
|
|
531
714
|
|
|
532
|
-
|
|
715
|
+
// Example: scaled container
|
|
716
|
+
<div style={{ transform: 'scale(0.5)' }}>
|
|
717
|
+
<ReactGridLayout positionStrategy={createScaledStrategy(0.5)} ... />
|
|
718
|
+
</div>
|
|
719
|
+
```
|
|
533
720
|
|
|
534
|
-
###
|
|
721
|
+
### Compactor
|
|
535
722
|
|
|
536
|
-
|
|
723
|
+
Layout compaction strategy. Built-in options:
|
|
537
724
|
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
//
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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;
|
|
550
762
|
}
|
|
551
|
-
// ...
|
|
552
763
|
```
|
|
553
764
|
|
|
554
|
-
|
|
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
|
+
```
|
|
555
785
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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;
|
|
564
848
|
}
|
|
565
849
|
```
|
|
566
850
|
|
|
567
|
-
|
|
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
|
+
};
|
|
568
896
|
|
|
569
|
-
|
|
897
|
+
// Usage
|
|
898
|
+
<GridLayout compactor={gravityCompactor} />
|
|
899
|
+
```
|
|
570
900
|
|
|
571
|
-
|
|
572
|
-
|
|
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
|
+
```
|
|
573
947
|
|
|
574
|
-
|
|
575
|
-
|
|
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";
|
|
576
963
|
```
|
|
577
964
|
|
|
578
|
-
###
|
|
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
|
+
```
|
|
579
1005
|
|
|
580
|
-
|
|
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
|
+
```
|
|
581
1049
|
|
|
582
|
-
|
|
583
|
-
|
|
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
|
+
```
|
|
584
1084
|
|
|
585
|
-
|
|
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();
|
|
586
1101
|
|
|
587
|
-
```js
|
|
588
|
-
const CustomGridItemComponent = React.forwardRef(({style, className, onMouseDown, onMouseUp, onTouchEnd, children, ...props}, ref) => {
|
|
589
1102
|
return (
|
|
590
|
-
<div
|
|
591
|
-
{
|
|
592
|
-
|
|
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
|
+
)}
|
|
593
1123
|
</div>
|
|
594
1124
|
);
|
|
595
|
-
}
|
|
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
|
+
}
|
|
596
1201
|
```
|
|
597
1202
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
+
```
|
|
601
1259
|
|
|
602
1260
|
## Contribute
|
|
603
1261
|
|
|
@@ -605,19 +1263,3 @@ If you have a feature request, please add it as an issue or make a pull request.
|
|
|
605
1263
|
|
|
606
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
|
|
607
1265
|
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
|