Skip to content

Commit c3f6805

Browse files
committed
Simplify react lib design
1 parent 90a014d commit c3f6805

23 files changed

+1278
-364
lines changed

react/README.md

Lines changed: 236 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -2,119 +2,280 @@
22

33
A React wrapper component for GridStack that provides better TypeScript support and React integration experience.
44

5+
Open in [CodeSandbox](https://codesandbox.io/p/sandbox/github/gridstack/gridstack.js/tree/master/react?file=/src/App.tsx)
6+
57
## TODO
68

7-
- [x] Component mapping
8-
- [x] SubGrid support
9-
- [ ] Save and restore layout
10-
- [ ] Publish to npm
9+
- [x] Add Widgets
10+
- [x] Add Sub Grid
11+
- [x] Nested Sub Grid
12+
- [x] Remove Widget
13+
- [x] Copy(Duplicate) Widget
14+
- [x] Custom handle
15+
- [ ] Drag between two grid stacks
1116

12-
## Basic Usage
17+
Welcome to give any suggestions and ideas, you can submit an issue or contact me by email. :)
1318

14-
This is not an npm package, it's just a demo project. Please copy the relevant code to your project to use it.
19+
## Usage
1520

16-
```tsx
17-
import {
18-
GridStackProvider,
19-
GridStackRender,
20-
GridStackRenderProvider,
21-
} from "path/to/lib";
22-
import "gridstack/dist/gridstack.css";
23-
import "gridstack/dist/gridstack-extra.css";
24-
import "path/to/demo.css";
25-
26-
function Text({ content }: { content: string }) {
27-
return <div>{content}</div>;
28-
}
21+
**Simple**
2922

30-
const COMPONENT_MAP = {
31-
Text,
32-
// ... other components
33-
};
34-
35-
// Grid options
36-
const gridOptions = {
37-
acceptWidgets: true,
38-
margin: 8,
39-
cellHeight: 50,
40-
children: [
41-
{
42-
id: "item1",
43-
h: 2,
44-
w: 2,
45-
content: JSON.stringify({
46-
name: "Text",
47-
props: { content: "Item 1" },
48-
}),
49-
},
50-
// ... other grid items
51-
],
52-
};
23+
Render item with widget id selector.
5324

25+
```tsx
5426
function App() {
27+
const [uncontrolledInitialOptions] = useState<GridStackOptions>({
28+
// ...
29+
children: [
30+
{ id: "item1", h: 2, w: 2, x: 0, y: 0 },
31+
{ id: "item2", h: 2, w: 2, x: 2, y: 0 },
32+
],
33+
});
34+
5535
return (
56-
<GridStackProvider initialOptions={gridOptions}>
57-
<!-- Maybe a toolbar here. Access to addWidget and addSubGrid by useGridStackContext() -->
36+
<GridStackProvider initialOptions={uncontrolledInitialOptions}>
37+
<Toolbar />
5838

59-
<!-- Grid Stack Root Element -->
60-
<GridStackRenderProvider>
61-
<!-- Grid Stack Default Render -->
62-
<GridStackRender componentMap={COMPONENT_MAP} />
63-
</GridStackRenderProvider>
39+
<GridStackRender>
40+
<GridStackItem id="item1">
41+
<div>hello</div>
42+
</GridStackItem>
6443

65-
<!-- Maybe other UI here -->
44+
<GridStackItem id="item2">
45+
<div>grid</div>
46+
</GridStackItem>
47+
</GridStackRender>
6648
</GridStackProvider>
6749
);
6850
}
6951
```
7052

71-
## Advanced Features
53+
**Advanced**
7254

73-
### Toolbar Operations
55+
Render item with widget map component info.
7456

75-
Provide APIs to add new components and sub-grids:
57+
_ComponentInfoMap is just an example, you can use any way you want to store and retrieve component information._
7658

7759
```tsx
78-
function Toolbar() {
79-
const { addWidget, addSubGrid } = useGridStackContext();
60+
function App() {
61+
const [uncontrolledInitialOptions] = useState<GridStackOptions>({
62+
// ...
63+
children: [
64+
{ id: "item1", h: 2, w: 2, x: 0, y: 0 },
65+
{ id: "item2", h: 2, w: 2, x: 2, y: 0 },
66+
],
67+
});
68+
69+
const [initialComponentInfoMap] = useState<Record<string, ComponentInfo>>(
70+
() => ({
71+
item1: { component: "Text", serializableProps: { content: "Text" } },
72+
item2: {
73+
component: "ComplexCard",
74+
serializableProps: { title: "Complex Card", color: "red" },
75+
},
76+
})
77+
);
8078

8179
return (
82-
<div>
83-
<button onClick={() => addWidget(/* ... */)}>Add Component</button>
84-
<button onClick={() => addSubGrid(/* ... */)}>Add SubGrid</button>
85-
</div>
80+
<ComponentInfoMapProvider initialComponentInfoMap={initialComponentInfoMap}>
81+
<GridStackProvider initialOptions={uncontrolledInitialOptions}>
82+
<Toolbar />
83+
84+
<GridStackRender>
85+
<DynamicGridStackItems />
86+
</GridStackRender>
87+
</GridStackProvider>
88+
</ComponentInfoMapProvider>
89+
);
90+
}
91+
92+
export function DynamicGridStackItems() {
93+
const { componentInfoMap } = useComponentInfoMap();
94+
95+
return (
96+
<>
97+
{Array.from(componentInfoMap.entries()).map(
98+
([widgetId, componentInfo]) => {
99+
const Component = COMPONENT_MAP[componentInfo.component];
100+
if (!Component) {
101+
throw new Error(`Component ${componentInfo.component} not found`);
102+
}
103+
104+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
105+
const props = componentInfo.serializableProps as any;
106+
107+
if (componentInfo.component === "ComplexCard") {
108+
return (
109+
<GridStackItem key={widgetId} id={widgetId}>
110+
<ComplexCardEditableWrapper
111+
key={`complex-card-editable-wrapper-${widgetId}`}
112+
serializableProps={componentInfo.serializableProps}
113+
>
114+
<Component {...props} key={`component-${widgetId}`} />
115+
</ComplexCardEditableWrapper>
116+
</GridStackItem>
117+
);
118+
}
119+
120+
return (
121+
<GridStackItem key={widgetId} id={widgetId}>
122+
<Component {...props} key={`component-${widgetId}`} />
123+
</GridStackItem>
124+
);
125+
}
126+
)}
127+
</>
86128
);
87129
}
88130
```
89131

90-
### Layout Saving
132+
**Experimental**
91133

92-
Get the current layout:
134+
Render item with custom handle.
93135

94136
```tsx
95-
const { saveOptions } = useGridStackContext();
96-
97-
const currentLayout = saveOptions();
137+
<GridStackItem id="xxx">
138+
<GridStackHandleReInitializer>
139+
<button className={CUSTOM_DRAGGABLE_HANDLE_CLASSNAME}>
140+
Handle ONLY HERE
141+
</button>
142+
</GridStackHandleReInitializer>
143+
</GridStackItem>
98144
```
99145

100146
## API Reference
101147

102-
### GridStackProvider
148+
### Components
103149

104-
The main context provider, accepts the following properties:
150+
#### GridStackProvider
105151

106-
- `initialOptions`: Initial configuration options for GridStack
152+
Top-level component that provides GridStack context.
107153

108-
### GridStackRender
154+
```typescript
155+
type GridStackProviderProps = {
156+
initialOptions: GridStackOptions; // GridStack initialization options
157+
children: React.ReactNode;
158+
};
159+
```
109160

110-
The core component for rendering the grid, accepts the following properties:
161+
#### GridStackRender
162+
163+
Render GridStack root container component.
164+
165+
```typescript
166+
type GridStackRenderProps = {
167+
children: React.ReactNode;
168+
};
169+
```
111170

112-
- `componentMap`: A mapping from component names to actual React components
171+
#### GridStackItem
172+
173+
Component representing a single grid item.
174+
175+
```typescript
176+
type GridStackItemProps = {
177+
id: string; // Grid item unique identifier
178+
children: React.ReactNode;
179+
};
180+
```
181+
182+
#### GridStackHandleReInitializer
183+
184+
Experimental component for reinitializing the drag handle of a grid item.
185+
186+
```typescript
187+
type GridStackHandleReInitializerProps = {
188+
children: React.ReactNode;
189+
};
190+
```
191+
192+
### Contexts
193+
194+
#### GridStackContext
195+
196+
Provide GridStack core functionality context.
197+
198+
```typescript
199+
interface GridStackContextType {
200+
initialOptions: GridStackOptions;
201+
addWidget: (widget: GridStackWidget) => void;
202+
removeWidget: (el: GridStackElement) => void;
203+
saveOptions: () => ReturnType<GridStack["save"]> | undefined;
204+
205+
_gridStack: {
206+
value: GridStack | null;
207+
set: React.Dispatch<React.SetStateAction<GridStack | null>>;
208+
};
209+
}
210+
```
211+
212+
#### GridStackItemContext
213+
214+
Provide single grid item functionality context.
215+
216+
```typescript
217+
type GridStackItemContextType = {
218+
id: string;
219+
// Native methods
220+
remove: () => void;
221+
update: (opt: GridStackWidget) => void;
222+
223+
// Extended methods
224+
getBounds: () => {
225+
current: { x?: number; y?: number; w?: number; h?: number };
226+
original: { x?: number; y?: number; w?: number; h?: number };
227+
} | null;
228+
setSize: (size: { w: number; h: number }) => void;
229+
setPosition: (position: { x: number; y: number }) => void;
230+
};
231+
```
232+
233+
#### GridStackRenderContext
234+
235+
Provide rendering related functionality context.
236+
237+
```typescript
238+
type GridStackRenderContextType = {
239+
getWidgetContainer: (widgetId: string) => HTMLElement | null;
240+
};
241+
```
113242

114243
### Hooks
115244

116-
- `useGridStackContext()`: Access GridStack context and operations
117-
- `addWidget`: Add a new component
118-
- `addSubGrid`: Add a new sub-grid
119-
- `saveOptions`: Save current layout
120-
- `initialOptions`: Initial configuration options
245+
#### useGridStackContext
246+
247+
Get GridStack context.
248+
249+
```typescript
250+
function useGridStackContext(): GridStackContextType;
251+
```
252+
253+
#### useGridStackItemContext
254+
255+
Get grid item context.
256+
257+
```typescript
258+
function useGridStackItemContext(): GridStackItemContextType;
259+
```
260+
261+
#### useGridStackRenderContext
262+
263+
Get rendering context.
264+
265+
```typescript
266+
function useGridStackRenderContext(): GridStackRenderContextType;
267+
```
268+
269+
### Type Exports
270+
271+
```typescript
272+
export type {
273+
GridStackContextType,
274+
GridStackProviderProps,
275+
GridStackRenderContextType,
276+
GridStackRenderProps,
277+
GridStackItemProps,
278+
GridStackItemContextType,
279+
GridStackHandleReInitializerProps,
280+
};
281+
```

0 commit comments

Comments
 (0)