@@ -36,6 +36,8 @@ type SidebarContextProps = {
3636 setOpen : ( open : boolean ) => void
3737 openMobile : boolean
3838 setOpenMobile : ( open : boolean ) => void
39+ closeMobileSidebar : ( ) => Promise < void >
40+ completeMobileSidebarClose : ( ) => void
3941 isMobile : boolean
4042 toggleSidebar : ( ) => void
4143}
@@ -66,6 +68,36 @@ function SidebarProvider({
6668} ) {
6769 const isMobile = useIsMobile ( )
6870 const [ openMobile , setOpenMobile ] = React . useState ( false )
71+ const mobileCloseResolversRef = React . useRef ( new Set < ( ) => void > ( ) )
72+
73+ const completeMobileSidebarClose = React . useCallback ( ( ) => {
74+ const resolvers = Array . from ( mobileCloseResolversRef . current )
75+ mobileCloseResolversRef . current . clear ( )
76+ resolvers . forEach ( ( resolve ) => resolve ( ) )
77+ } , [ ] )
78+
79+ const closeMobileSidebar = React . useCallback ( ( ) => {
80+ if ( ! isMobile || ! openMobile ) {
81+ return Promise . resolve ( )
82+ }
83+
84+ setOpenMobile ( false )
85+
86+ return new Promise < void > ( ( resolve ) => {
87+ let timeoutId : number | undefined
88+ const resolveOnce = ( ) => {
89+ if ( timeoutId !== undefined ) {
90+ window . clearTimeout ( timeoutId )
91+ }
92+ mobileCloseResolversRef . current . delete ( resolveOnce )
93+ resolve ( )
94+ }
95+
96+ mobileCloseResolversRef . current . add ( resolveOnce )
97+ // Fallback for environments that do not fire CSS animation events.
98+ timeoutId = window . setTimeout ( resolveOnce , 350 )
99+ } )
100+ } , [ isMobile , openMobile ] )
69101
70102 const getInitialOpen = ( ) => {
71103 if ( typeof document === "undefined" ) return defaultOpen
@@ -131,9 +163,20 @@ function SidebarProvider({
131163 isMobile,
132164 openMobile,
133165 setOpenMobile,
166+ closeMobileSidebar,
167+ completeMobileSidebarClose,
134168 toggleSidebar,
135169 } ) ,
136- [ state , open , setOpen , isMobile , openMobile , toggleSidebar ] ,
170+ [
171+ state ,
172+ open ,
173+ setOpen ,
174+ isMobile ,
175+ openMobile ,
176+ closeMobileSidebar ,
177+ completeMobileSidebarClose ,
178+ toggleSidebar ,
179+ ] ,
137180 )
138181
139182 return (
@@ -173,7 +216,13 @@ function Sidebar({
173216 variant ?: "sidebar" | "floating" | "inset"
174217 collapsible ?: "offcanvas" | "icon" | "none"
175218} ) {
176- const { isMobile, state, openMobile, setOpenMobile } = useSidebar ( )
219+ const {
220+ isMobile,
221+ state,
222+ openMobile,
223+ setOpenMobile,
224+ completeMobileSidebarClose,
225+ } = useSidebar ( )
177226
178227 if ( collapsible === "none" ) {
179228 return (
@@ -198,6 +247,11 @@ function Sidebar({
198247 data-slot = "sidebar"
199248 data-mobile = "true"
200249 className = "bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden"
250+ onAnimationEnd = { ( event ) => {
251+ if ( event . currentTarget === event . target && ! openMobile ) {
252+ completeMobileSidebarClose ( )
253+ }
254+ } }
201255 style = {
202256 {
203257 "--sidebar-width" : SIDEBAR_WIDTH_MOBILE ,
0 commit comments