1
- // gridstack-context.tsx
2
- "use client" ;
3
-
4
- import * as React from "react" ;
5
- import type { GridStack } from "gridstack" ;
6
- import "gridstack/dist/gridstack-extra.css" ;
7
- import "gridstack/dist/gridstack.css" ;
8
- import type { ItemRefType } from "./gridstack-item" ;
1
+ import { GridStack , GridStackWidget , GridStackOptions } from 'gridstack' ;
2
+ import React , {
3
+ createContext ,
4
+ PropsWithChildren ,
5
+ useCallback ,
6
+ useLayoutEffect ,
7
+ useMemo ,
8
+ useRef ,
9
+ useState ,
10
+ } from 'react' ;
11
+ import isEqual from 'react-fast-compare' ;
9
12
10
13
type GridStackContextType = {
11
- grid : GridStack | null | undefined ;
12
- setGrid : React . Dispatch < React . SetStateAction < GridStack | null > > ;
13
- addItemRefToList : ( id : string , ref : ItemRefType ) => void ;
14
- removeItemRefFromList : ( id : string ) => void ;
15
- itemRefList : ItemRefListType ;
16
- getItemRefFromListById : ( id : string ) => ItemRefType | null ;
14
+ getWidgetContent : ( widgetId : string ) => HTMLElement | null ;
17
15
} ;
18
16
19
- type ItemRefListType = {
20
- id : string ;
21
- ref : ItemRefType ;
22
- } [ ] ;
17
+ interface GridstackProviderProps extends PropsWithChildren {
18
+ options : GridStackOptions ;
19
+ }
20
+
21
+ export const GridstackContext = createContext < GridStackContextType | null > ( null ) ;
23
22
24
- export const GridstackContext = React . createContext < GridStackContextType | null > ( null ) ;
23
+ export const GridstackProvider = ( { children, options } : GridstackProviderProps ) => {
24
+ const widgetContentRef = useRef < Record < string , HTMLElement > > ( { } ) ;
25
+ const containerRef = useRef < HTMLDivElement > ( null ) ;
26
+ const optionsRef = useRef < GridStackOptions > ( options ) ;
25
27
26
- export const GridstackProvider = ( { children } : { children : React . ReactNode } ) => {
27
- const [ grid , setGrid ] = React . useState < GridStack | null > ( null ) ;
28
- const [ itemRefList , setItemRefList ] = React . useState < ItemRefListType > ( [ ] ) ;
28
+ const [ parentGrid , setParentGrid ] = useState < GridStack | null > ( null ) ;
29
29
30
- const addItemRefToList = React . useCallback ( ( id : string , ref : ItemRefType ) => {
31
- setItemRefList ( ( prev ) => [ ...prev , { id, ref } ] ) ;
30
+ const renderCBFn = useCallback ( ( element : HTMLElement , widget : GridStackWidget ) => {
31
+ if ( widget . id ) {
32
+ widgetContentRef . current [ widget . id ] = element ;
33
+ }
32
34
} , [ ] ) ;
33
35
34
- const removeItemRefFromList = React . useCallback ( ( id : string ) => {
35
- setItemRefList ( ( prev ) => prev . filter ( ( item ) => item . id !== id ) ) ;
36
+ const getWidgetContent = useCallback ( ( widgetId : string ) => {
37
+ return widgetContentRef . current [ widgetId ] || null ;
36
38
} , [ ] ) ;
37
39
38
- const getItemRefFromListById = React . useCallback ( ( id : string ) => {
39
- const item = itemRefList . find ( ( item ) => item . id === id ) ;
40
- return item ?. ref ?? null ;
41
- } , [ itemRefList ] ) ;
40
+ const initGrid = useCallback ( ( ) => {
41
+ if ( containerRef . current ) {
42
+ GridStack . renderCB = renderCBFn ;
43
+ return GridStack . init ( optionsRef . current , containerRef . current ) ;
44
+ }
45
+ return null ;
46
+ } , [ renderCBFn ] ) ;
47
+
48
+ useLayoutEffect ( ( ) => {
49
+ if ( ! isEqual ( options , optionsRef . current ) && parentGrid ) {
50
+ try {
51
+ parentGrid . removeAll ( false ) ;
52
+ parentGrid . destroy ( false ) ;
53
+ widgetContentRef . current = { } ;
54
+ optionsRef . current = options ;
55
+
56
+ setParentGrid ( initGrid ( ) ) ;
57
+ } catch ( e ) {
58
+ console . error ( "Error reinitializing gridstack" , e ) ;
59
+ }
60
+ }
61
+ } , [ options , parentGrid , initGrid ] ) ;
62
+
63
+ useLayoutEffect ( ( ) => {
64
+ if ( ! parentGrid ) {
65
+ try {
66
+ setParentGrid ( initGrid ( ) ) ;
67
+ } catch ( e ) {
68
+ console . error ( "Error initializing gridstack" , e ) ;
69
+ }
70
+ }
71
+ } , [ parentGrid , initGrid ] ) ;
42
72
43
- // Memoize the context value to prevent unnecessary re-renders
44
- const value = React . useMemo (
73
+ const value = useMemo (
45
74
( ) => ( {
46
- grid,
47
- setGrid,
48
- addItemRefToList,
49
- removeItemRefFromList,
50
- itemRefList,
51
- getItemRefFromListById,
75
+ getWidgetContent,
52
76
} ) ,
53
- [ grid , itemRefList , addItemRefToList , removeItemRefFromList , getItemRefFromListById ]
77
+ // parentGrid is required to reinitialize the grid when the options change
78
+ [ getWidgetContent , parentGrid ] ,
54
79
) ;
55
80
56
81
return (
57
82
< GridstackContext . Provider value = { value } >
58
- { children }
83
+ < div ref = { containerRef } > { parentGrid ? children : null } </ div >
59
84
</ GridstackContext . Provider >
60
85
) ;
61
86
} ;
0 commit comments