@@ -9,17 +9,23 @@ import { DockerIcon } from "components/Icons/DockerIcon";
9
9
import { type CSSProperties , type ElementType , type FC , useState } from "react" ;
10
10
import type { FileTree } from "utils/filetree" ;
11
11
12
- const sortFileTree = ( fileTree :FileTree ) => ( a :string , b :string ) => {
13
- const contentA = fileTree [ a ] ;
14
- const contentB = fileTree [ b ] ;
15
- if ( typeof contentA === "object" ) {
16
- return - 1 ;
17
- }
18
- if ( typeof contentB === "object" ) {
19
- return 1 ;
12
+ const isFolder = ( content ?:FileTree | string ) :content isFileTree =>
13
+ typeof content === "object" ;
14
+
15
+ type FileTreeEntry = [ key :string , content :FileTree | string ] ;
16
+ function compareFileTreeEntries (
17
+ [ keyA , contentA ] :FileTreeEntry ,
18
+ [ keyB , contentB ] :FileTreeEntry ,
19
+ ) {
20
+ // A and B are either both files or both folders, so they should be sorted
21
+ // lexically.
22
+ if ( isFolder ( contentA ) === isFolder ( contentB ) ) {
23
+ return keyA . localeCompare ( keyB ) ;
20
24
}
21
- return a . localeCompare ( b ) ;
22
- } ;
25
+ // Either A or B is a folder, and the other is a file. Put whichever one is a
26
+ // folder first.
27
+ return isFolder ( contentA ) ?- 1 :1 ;
28
+ }
23
29
24
30
type ContextMenu = {
25
31
path :string ;
@@ -51,9 +57,6 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
51
57
} ) => {
52
58
const [ contextMenu , setContextMenu ] = useState < ContextMenu | undefined > ( ) ;
53
59
54
- const isFolder = ( content ?:FileTree | string ) :content isFileTree =>
55
- typeof content === "object" ;
56
-
57
60
const buildTreeItems = (
58
61
label :string ,
59
62
filename :string ,
@@ -181,12 +184,11 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
181
184
}
182
185
>
183
186
{ isFolder ( content ) &&
184
- Object . keys ( content )
185
- . sort ( sortFileTree ( content ) )
186
- . map ( ( filename ) => {
187
- const child = content [ filename ] ;
188
- return buildTreeItems ( filename , filename , child , currentPath ) ;
189
- } ) }
187
+ Object . entries ( content )
188
+ . sort ( compareFileTreeEntries )
189
+ . map ( ( [ filename , child ] ) =>
190
+ buildTreeItems ( filename , filename , child , currentPath ) ,
191
+ ) }
190
192
</ TreeItem >
191
193
) ;
192
194
} ;
@@ -198,12 +200,9 @@ export const TemplateFileTree: FC<TemplateFilesTreeProps> = ({
198
200
defaultExpandedItems = { activePath ?expandablePaths ( activePath ) :[ ] }
199
201
defaultSelectedItems = { activePath }
200
202
>
201
- { Object . keys ( fileTree )
202
- . sort ( sortFileTree ( fileTree ) )
203
- . map ( ( filename ) => {
204
- const child = fileTree [ filename ] ;
205
- return buildTreeItems ( filename , filename , child ) ;
206
- } ) }
203
+ { Object . entries ( fileTree )
204
+ . sort ( compareFileTreeEntries )
205
+ . map ( ( [ filename , child ] ) => buildTreeItems ( filename , filename , child ) ) }
207
206
208
207
< Menu
209
208
onClose = { ( ) => setContextMenu ( undefined ) }