@@ -5,6 +5,14 @@ import { ChevronRight, ChevronDown, Folder } from "lucide-react";
55import { SolidStorage } from "../lib/hooks/useSolidStorages" ;
66import { FolderTreeChild , folderUrlsEqual , ensureTrailingSlash , getAuthenticatedSession , fetchContainerListing , foldersFromListing } from "../lib/helpers" ;
77import { getContainerListing , loadContainerListing , subscribeContainerCache , getContainerCacheVersion } from "../lib/cache" ;
8+ import {
9+ SidebarMenu ,
10+ SidebarMenuButton ,
11+ SidebarMenuItem ,
12+ SidebarMenuSub ,
13+ SidebarMenuSubItem ,
14+ SidebarMenuSkeleton ,
15+ } from "@/components/ui/sidebar" ;
816
917interface FolderTreeProps {
1018 storages : SolidStorage [ ] ;
@@ -30,6 +38,8 @@ export default function FolderTree({
3038 // Children come from the shared cache, not a local copy.
3139 // cacheVersion makes this recompute when listings are written or invalidated.
3240 const childrenByUrl = useMemo ( ( ) => {
41+ // cacheVersion is read so listings recompute after cache writes/invalidation.
42+ void cacheVersion ;
3343 const next : Record < string , FolderTreeChild [ ] > = { } ;
3444 for ( const url of expandedUrls ) {
3545 const cached = getContainerListing ( url ) ;
@@ -124,7 +134,7 @@ export default function FolderTree({
124134 } , [ expandedUrls , loadChildren ] ) ;
125135
126136 // Render one folder row and its nested children when expanded.
127- const renderNode = useCallback ( ( node : FolderTreeChild , depth : number ) => {
137+ const renderNode = useCallback ( ( node : FolderTreeChild ) => {
128138 const nodeUrl = ensureTrailingSlash ( node . url ) ;
129139 const isExpanded = expandedUrls . has ( nodeUrl ) ;
130140 const isLoading = loadingUrls . has ( nodeUrl ) ;
@@ -133,16 +143,12 @@ export default function FolderTree({
133143 const isCurrent = normalizedCurrentFolderUrl != null && folderUrlsEqual ( normalizedCurrentFolderUrl , nodeUrl ) ;
134144
135145 return (
136- < li key = { nodeUrl } >
137- < div
138- className = { `group flex items-center gap-1 rounded-md px-2 py-1 text-sm ${ isCurrent ? "bg-accent text-foreground font-medium" : "text-foreground hover:bg-muted"
139- } `}
140- style = { { paddingLeft : `${ depth * 12 + 8 } px` } }
141- >
146+ < SidebarMenuItem key = { nodeUrl } >
147+ < div className = "flex w-full items-center gap-0.5" >
142148 < button
143149 type = "button"
144150 onClick = { ( ) => void toggleExpand ( nodeUrl ) }
145- className = "rounded p-0.5 hover:bg-muted "
151+ className = "flex size-7 shrink-0 items-center justify-center rounded-md text-sidebar-foreground hover:bg-sidebar-accent "
146152 aria-label = { isExpanded ? `Collapse ${ node . name } ` : `Expand ${ node . name } ` }
147153 aria-expanded = { isExpanded }
148154 >
@@ -153,44 +159,41 @@ export default function FolderTree({
153159 ) }
154160 </ button >
155161
156- < button
157- type = "button"
162+ < SidebarMenuButton
163+ isActive = { isCurrent }
164+ className = "flex-1"
158165 onClick = { ( ) => onNavigate ( nodeUrl ) }
159- className = "flex min-w-0 flex-1 items-center gap-2 text-left"
160166 title = { node . name }
161167 aria-current = { isCurrent ? "page" : undefined }
162168 >
163- < Folder className = "h-4 w-4 shrink-0" aria-hidden = "true" />
164- < span className = "truncate" > { node . name } </ span >
165- </ button >
169+ < Folder aria-hidden = "true" />
170+ < span > { node . name } </ span >
171+ </ SidebarMenuButton >
166172 </ div >
167173
168- { isExpanded && (
169- < ul className = "space-y-0.5" >
174+ { isExpanded && ( isLoading || hasError || children . length > 0 ) && (
175+ < SidebarMenuSub >
170176 { isLoading && (
171- < li
172- className = "px-2 py-1 text-xs text-muted-foreground"
173- style = { { paddingLeft : `${ ( depth + 1 ) * 12 + 8 } px` } }
174- aria-live = "polite"
175- >
176- Loading...
177- </ li >
177+ < SidebarMenuSubItem >
178+ < SidebarMenuSkeleton showIcon />
179+ </ SidebarMenuSubItem >
178180 ) }
179181
180182 { ! isLoading && hasError && (
181- < li
182- className = "px-2 py-1 text-xs text-destructive"
183- style = { { paddingLeft : `${ ( depth + 1 ) * 12 + 8 } px` } }
184- >
185- Failed to load folders
186- </ li >
183+ < SidebarMenuSubItem >
184+ < p className = "px-2 py-1 text-xs text-destructive" >
185+ Failed to load folders
186+ </ p >
187+ </ SidebarMenuSubItem >
187188 ) }
188189
189- { ! isLoading && ! hasError && children . map ( ( child ) => renderNode ( child , depth + 1 ) ) }
190- </ ul >
190+ { ! isLoading &&
191+ ! hasError &&
192+ children . map ( ( child ) => renderNode ( child ) ) }
193+ </ SidebarMenuSub >
191194 ) }
192- </ li >
193- )
195+ </ SidebarMenuItem >
196+ ) ;
194197 } , [
195198 childrenByUrl ,
196199 errorByUrl ,
@@ -212,12 +215,15 @@ export default function FolderTree({
212215 ) ;
213216
214217 if ( rootNodes . length === 0 ) {
215- return < p className = "px-3 py-2 text-sm text-muted-foreground" > No storages found</ p >
218+ return (
219+ < p className = "px-3 py-2 text-sm text-muted-foreground" >
220+ No storages found
221+ </ p >
222+ ) ;
216223 }
217-
218224 return (
219- < ul className = "space-y-0.5" aria-label = "My Storages" >
220- { rootNodes . map ( ( node ) => renderNode ( node , 0 ) ) }
221- </ ul >
225+ < SidebarMenu aria-label = "My Storages" >
226+ { rootNodes . map ( ( node ) => renderNode ( node ) ) }
227+ </ SidebarMenu >
222228 ) ;
223229}
0 commit comments