1
- import { useState } from "react" ;
1
+ import { useMemo , useState } from "react" ;
2
2
import { useHotkeys } from "react-hotkeys-hook" ;
3
3
import {
4
4
Button ,
@@ -21,10 +21,13 @@ import { t } from "i18next";
21
21
import { Key } from "ts-key-enum" ;
22
22
23
23
import {
24
+ AscendingIcon ,
25
+ DescendingIcon ,
24
26
LinkIcon ,
25
27
OutlineViewOnIcon ,
26
28
RecycleDeleteIcon ,
27
29
RefreshIcon ,
30
+ SortingIcon ,
28
31
UploadIcon ,
29
32
} from "@/components/CommonIcon" ;
30
33
import ConfirmButton from "@/components/ConfirmButton" ;
@@ -45,6 +48,33 @@ import UploadButton from "../UploadButton";
45
48
46
49
import useAwsS3 from "@/hooks/useAwsS3" ;
47
50
import useGlobalStore from "@/pages/globalStore" ;
51
+
52
+ type TOrderType = null | "ascFilename" | "descFilename" | "ascFileType" | "descFileType" | "ascSize" | "descSize" | "ascDate" | "descDate"
53
+
54
+ const SortIcon = ( { currentOrderType, orderTypeAsc, orderTypeDesc, onSort } : {
55
+ currentOrderType : TOrderType ,
56
+ orderTypeAsc : TOrderType ,
57
+ orderTypeDesc : TOrderType ,
58
+ onSort : ( ) => void
59
+ } ) => {
60
+ const isAsc = currentOrderType === orderTypeAsc ;
61
+ const isDesc = currentOrderType === orderTypeDesc ;
62
+
63
+ let icon = < SortingIcon boxSize = { 3 } className = "absolute top-[1.5px]" /> ;
64
+
65
+ if ( isAsc ) {
66
+ icon = < AscendingIcon boxSize = { 3 } className = "absolute top-[1.5px]" /> ;
67
+ } else if ( isDesc ) {
68
+ icon = < DescendingIcon boxSize = { 3 } className = "absolute top-[1.5px]" /> ;
69
+ }
70
+
71
+ return (
72
+ < span onClick = { onSort } className = "cursor-pointer relative" >
73
+ { icon }
74
+ </ span >
75
+ ) ;
76
+ } ;
77
+
48
78
export default function FileList ( ) {
49
79
const { getList, getFileUrl, deleteFile } = useAwsS3 ( ) ;
50
80
const { currentStorage, prefix, setPrefix, markerArray, setMarkerArray, getFilePath } =
@@ -60,6 +90,8 @@ export default function FileList() {
60
90
const confirmDialog = useConfirmDialog ( ) ;
61
91
const { showError, showLoading, showSuccess } = useGlobalStore ( ) ;
62
92
93
+ const [ orderType , setOrderType ] = useState < TOrderType > ( null ) ;
94
+
63
95
const {
64
96
data : queryData ,
65
97
refetch,
@@ -77,6 +109,74 @@ export default function FileList() {
77
109
} ,
78
110
) ;
79
111
112
+ const compareFilename = ( a : any , b : any ) => {
113
+ if ( a . Key && b . Key ) {
114
+ return a . Key . localeCompare ( b . Key ) ;
115
+ } else if ( a . Key ) {
116
+ return 1 ;
117
+ } else if ( b . Key ) {
118
+ return - 1 ;
119
+ } else {
120
+ return a . Prefix . localeCompare ( b . Prefix ) ;
121
+ }
122
+ }
123
+
124
+ const compareFileType = ( a : any , b : any ) => {
125
+ const fileNameA = a . Key ?. split ( '/' ) ;
126
+ const fileNameB = b . Key ?. split ( '/' ) ;
127
+
128
+ if ( ! fileNameA && fileNameB ) return 1 ;
129
+ if ( fileNameA && ! fileNameB ) return - 1 ;
130
+ if ( ! fileNameA && ! fileNameB ) return 0 ;
131
+
132
+ return formateType ( fileNameA [ fileNameA . length - 1 ] )
133
+ . localeCompare ( formateType ( fileNameB [ fileNameB . length - 1 ] ) ) ;
134
+ }
135
+
136
+ const compareDate = ( a : any , b : any ) => {
137
+ const dateA = new Date ( a . LastModified ) ;
138
+ const dateB = new Date ( b . LastModified ) ;
139
+
140
+ if ( dateA > dateB ) {
141
+ return 1 ;
142
+ } else if ( dateA < dateB ) {
143
+ return - 1 ;
144
+ } else {
145
+ return 0 ;
146
+ }
147
+ }
148
+
149
+ const sortData = ( data : any , orderType : TOrderType ) => {
150
+ if ( ! data ) return [ ] ;
151
+
152
+ const sorted = [ ...data ] . sort ( ( a , b ) => {
153
+ switch ( orderType ) {
154
+ case 'ascFilename' :
155
+ return compareFilename ( a , b ) ;
156
+ case 'descFilename' :
157
+ return compareFilename ( b , a ) ;
158
+ case 'ascFileType' :
159
+ return compareFileType ( a , b ) ;
160
+ case 'descFileType' :
161
+ return compareFileType ( b , a ) ;
162
+ case 'ascSize' :
163
+ return ( a . Size || 0 ) - ( b . Size || 0 ) ;
164
+ case 'descSize' :
165
+ return ( b . Size || 0 ) - ( a . Size || 0 ) ;
166
+ case 'ascDate' :
167
+ return compareDate ( a , b ) ;
168
+ case 'descDate' :
169
+ return compareDate ( b , a ) ;
170
+ default :
171
+ return 0 ;
172
+ }
173
+ } ) ;
174
+
175
+ return sorted ;
176
+ }
177
+
178
+ const renderData = useMemo ( ( ) => sortData ( queryData ?. data , orderType ) , [ queryData , orderType ] ) ;
179
+
80
180
useHotkeys (
81
181
[ "ctrl+a" , "meta+a" ] ,
82
182
( e ) => {
@@ -260,17 +360,49 @@ export default function FileList() {
260
360
} ) }
261
361
>
262
362
< Tr >
263
- < Th > { t ( "StoragePanel.FileName" ) } </ Th >
264
- < Th > { t ( "StoragePanel.FileType" ) } </ Th >
265
- < Th > { t ( "StoragePanel.Size" ) } </ Th >
266
- < Th > { t ( "StoragePanel.Time" ) } </ Th >
363
+ < Th >
364
+ < span > { t ( "StoragePanel.FileName" ) } </ span >
365
+ < SortIcon
366
+ currentOrderType = { orderType }
367
+ orderTypeAsc = "ascFilename"
368
+ orderTypeDesc = "descFilename"
369
+ onSort = { ( ) => setOrderType ( orderType === "ascFilename" ? "descFilename" : "ascFilename" ) }
370
+ />
371
+ </ Th >
372
+ < Th >
373
+ < span > { t ( "StoragePanel.FileType" ) } </ span >
374
+ < SortIcon
375
+ currentOrderType = { orderType }
376
+ orderTypeAsc = "ascFileType"
377
+ orderTypeDesc = "descFileType"
378
+ onSort = { ( ) => setOrderType ( orderType === "ascFileType" ? "descFileType" : "ascFileType" ) }
379
+ />
380
+ </ Th >
381
+ < Th >
382
+ < span > { t ( "StoragePanel.Size" ) } </ span >
383
+ < SortIcon
384
+ currentOrderType = { orderType }
385
+ orderTypeAsc = "ascSize"
386
+ orderTypeDesc = "descSize"
387
+ onSort = { ( ) => setOrderType ( orderType === "ascSize" ? "descSize" : "ascSize" ) }
388
+ />
389
+ </ Th >
390
+ < Th >
391
+ < span > { t ( "StoragePanel.Time" ) } </ span >
392
+ < SortIcon
393
+ currentOrderType = { orderType }
394
+ orderTypeAsc = "ascDate"
395
+ orderTypeDesc = "descDate"
396
+ onSort = { ( ) => setOrderType ( orderType === "ascDate" ? "descDate" : "ascDate" ) }
397
+ />
398
+ </ Th >
267
399
< Th isNumeric >
268
400
< span className = "mr-2" > { t ( "Operation" ) } </ span >
269
401
</ Th >
270
402
</ Tr >
271
403
</ Thead >
272
404
< Tbody className = "text-grayModern-500" >
273
- { queryData . data . map ( ( file : TFile ) => {
405
+ { renderData ? .map ( ( file : TFile ) => {
274
406
const fileName = file . Key ?. split ( "/" ) ;
275
407
const dirName = file . Prefix ?. split ( "/" ) || [ ] ;
276
408
const fileType = file . Prefix
0 commit comments