@@ -20,6 +20,7 @@ import {
2020 useCallback ,
2121 useMemo ,
2222 useEffect ,
23+ useRef ,
2324} from 'react' ;
2425import { CustomDrawer } from './CustomDrawer' ;
2526
@@ -33,6 +34,7 @@ export interface DrawerPartialState {
3334 isDrawerOpen : boolean ;
3435 drawerWidth : number ;
3536 setDrawerWidth : ( width : number ) => void ;
37+ closeDrawer : ( ) => void ;
3638}
3739
3840/**
@@ -69,12 +71,12 @@ export interface ApplicationDrawerProps {
6971 drawerContents : DrawerContentType [ ] ;
7072 /**
7173 * Array of state exposer components from drawer plugins
72- * These are typically mounted via `application/drawer-state` mount point
74+ * These are typically mounted via `application/internal/ drawer-state` mount point
7375 *
7476 * In RHDH dynamic plugins, this would come from:
7577 * ```yaml
7678 * mountPoints:
77- * - mountPoint: application/drawer-state
79+ * - mountPoint: application/internal/ drawer-state
7880 * importName: TestDrawerStateExposer
7981 * ```
8082 */
@@ -85,55 +87,85 @@ export const ApplicationDrawer = ({
8587 drawerContents,
8688 stateExposers = [ ] ,
8789} : ApplicationDrawerProps ) => {
88- // Collect drawer states from all state exposers
89- const [ drawerStates , setDrawerStates ] = useState <
90- Record < string , DrawerPartialState >
91- > ( { } ) ;
92-
93- // Callback for state exposers to report their state
94- const handleStateChange = useCallback ( ( state : DrawerPartialState ) => {
95- setDrawerStates ( prev => {
96- // Only update if something actually changed
97- const existing = prev [ state . id ] ;
98- if (
99- existing &&
100- existing . isDrawerOpen === state . isDrawerOpen &&
101- existing . drawerWidth === state . drawerWidth &&
102- existing . setDrawerWidth === state . setDrawerWidth
90+ const drawerStatesRef = useRef < Map < string , DrawerPartialState > > ( new Map ( ) ) ;
91+ const [ , forceUpdate ] = useState ( { } ) ;
92+ const [ activeDrawerId , setActiveDrawerId ] = useState < string | null > ( null ) ;
93+
94+ const handleStateChange = useCallback (
95+ ( state : DrawerPartialState ) => {
96+ const prev = drawerStatesRef . current . get ( state . id ) ;
97+ const hasChanged =
98+ ! prev ||
99+ prev . isDrawerOpen !== state . isDrawerOpen ||
100+ prev . drawerWidth !== state . drawerWidth ||
101+ prev . setDrawerWidth !== state . setDrawerWidth ;
102+
103+ // If drawer just opened then make it the active drawer
104+ if ( ! prev ?. isDrawerOpen && state . isDrawerOpen ) {
105+ setActiveDrawerId ( state . id ) ;
106+ }
107+ // If drawer just closed and it was the active one, clear active drawer
108+ else if (
109+ prev ?. isDrawerOpen &&
110+ ! state . isDrawerOpen &&
111+ state . id === activeDrawerId
103112 ) {
104- return prev ;
113+ setActiveDrawerId ( null ) ;
114+ }
115+
116+ drawerStatesRef . current . set ( state . id , state ) ;
117+
118+ if ( hasChanged ) {
119+ forceUpdate ( { } ) ;
105120 }
106- return { ...prev , [ state . id ] : state } ;
107- } ) ;
108- } , [ ] ) ;
109-
110- // Convert states record to array
111- const statesArray = useMemo (
112- ( ) => Object . values ( drawerStates ) ,
113- [ drawerStates ] ,
121+ } ,
122+ [ activeDrawerId ] ,
123+ ) ;
124+
125+ const drawerStates = Array . from ( drawerStatesRef . current . values ( ) ) ;
126+
127+ const allDrawers = useMemo (
128+ ( ) =>
129+ drawerStates
130+ . map ( state => {
131+ const content = drawerContents . find ( c => c . id === state . id ) ;
132+ if ( ! content ) return null ;
133+
134+ return {
135+ state,
136+ Component : content . Component ,
137+ priority : content . priority ,
138+ } ;
139+ } )
140+ . filter ( Boolean ) ,
141+ [ drawerStates , drawerContents ] ,
114142 ) ;
115143
116- // Get active drawer - find the open drawer with highest priority
117- const activeDrawer = useMemo ( ( ) => {
118- return statesArray
119- . filter ( state => state . isDrawerOpen )
120- . map ( state => {
121- const content = drawerContents . find ( c => c . id === state . id ) ;
122- if ( ! content ) return null ;
123- return { ...state , ...content } ;
124- } )
125- . filter ( Boolean )
126- . sort ( ( a , b ) => ( b ?. priority ?? - 1 ) - ( a ?. priority ?? - 1 ) ) [ 0 ] ;
127- } , [ statesArray , drawerContents ] ) ;
144+ const activeDrawer =
145+ allDrawers . find ( d => d ?. state . id === activeDrawerId ) || null ;
146+
147+ // Close all other drawers when one becomes active
148+ useEffect ( ( ) => {
149+ if ( activeDrawerId ) {
150+ drawerStates . forEach ( state => {
151+ if ( state . id !== activeDrawerId && state . isDrawerOpen ) {
152+ state . closeDrawer ( ) ;
153+ }
154+ } ) ;
155+ }
156+ } , [ activeDrawerId , drawerStates ] ) ;
128157
129158 // Manage CSS classes and variables for layout adjustments
130159 useEffect ( ( ) => {
131160 if ( activeDrawer ) {
132- const className = ` docked-drawer-open` ;
133- const cssVar = ` --docked-drawer-width` ;
161+ const className = ' docked-drawer-open' ;
162+ const cssVar = ' --docked-drawer-width' ;
134163
135164 document . body . classList . add ( className ) ;
136- document . body . style . setProperty ( cssVar , `${ activeDrawer . drawerWidth } px` ) ;
165+ document . body . style . setProperty (
166+ cssVar ,
167+ `${ activeDrawer . state . drawerWidth } px` ,
168+ ) ;
137169
138170 return ( ) => {
139171 document . body . classList . remove ( className ) ;
@@ -142,32 +174,24 @@ export const ApplicationDrawer = ({
142174 }
143175 return undefined ;
144176 // eslint-disable-next-line react-hooks/exhaustive-deps
145- } , [ activeDrawer ?. id , activeDrawer ?. drawerWidth ] ) ;
146-
147- // Wrapper to handle the width change callback type
148- const handleWidthChange = useCallback (
149- ( width : number ) => {
150- activeDrawer ?. setDrawerWidth ( width ) ;
151- } ,
152- [ activeDrawer ] ,
153- ) ;
177+ } , [ activeDrawer ?. state . id , activeDrawer ?. state . drawerWidth ] ) ;
154178
155179 return (
156180 < >
157181 { /* Render all state exposers - they return null but report their state */ }
158182 { stateExposers . map ( ( { Component } , index ) => (
159183 < Component
160- key = { `${ index } -${ Component . displayName } ` }
184+ key = { `drawer -${ Component . displayName || index } ` }
161185 onStateChange = { handleStateChange }
162186 />
163187 ) ) }
164188
165189 { /* Render the active drawer */ }
166190 { activeDrawer && (
167191 < CustomDrawer
168- isDrawerOpen
169- drawerWidth = { activeDrawer . drawerWidth }
170- onWidthChange = { handleWidthChange }
192+ isDrawerOpen = { activeDrawer . state . isDrawerOpen }
193+ drawerWidth = { activeDrawer . state . drawerWidth }
194+ onWidthChange = { activeDrawer . state . setDrawerWidth }
171195 >
172196 < activeDrawer . Component />
173197 </ CustomDrawer >
0 commit comments