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' ;
912
1013type 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 ;
1715} ;
1816
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 ) ;
2322
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 ) ;
2527
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 ) ;
2929
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+ }
3234 } , [ ] ) ;
3335
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 ;
3638 } , [ ] ) ;
3739
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 ] ) ;
4272
43- // Memoize the context value to prevent unnecessary re-renders
44- const value = React . useMemo (
73+ const value = useMemo (
4574 ( ) => ( {
46- grid,
47- setGrid,
48- addItemRefToList,
49- removeItemRefFromList,
50- itemRefList,
51- getItemRefFromListById,
75+ getWidgetContent,
5276 } ) ,
53- [ grid , itemRefList , addItemRefToList , removeItemRefFromList , getItemRefFromListById ]
77+ // parentGrid is required to reinitialize the grid when the options change
78+ [ getWidgetContent , parentGrid ] ,
5479 ) ;
5580
5681 return (
5782 < GridstackContext . Provider value = { value } >
58- { children }
83+ < div ref = { containerRef } > { parentGrid ? children : null } </ div >
5984 </ GridstackContext . Provider >
6085 ) ;
6186} ;
0 commit comments