Skip to content

Commit a9ff4e8

Browse files
committed
Put back items sorting
Signed-off-by: Trung Nguyen <[email protected]>
1 parent aa6b989 commit a9ff4e8

File tree

2 files changed

+98
-31
lines changed

2 files changed

+98
-31
lines changed

src/extension/ui/src/components/CatalogGrid.tsx

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { createDockerDesktopClient } from '@docker/extension-api-client';
22
import { ExecResult } from '@docker/extension-api-client-types/dist/v0';
33
import FolderOpenRounded from '@mui/icons-material/FolderOpenRounded';
4-
import { Alert, AlertTitle, Badge, Box, Button, Checkbox, CircularProgress, Dialog, DialogContent, DialogTitle, FormControlLabel, FormGroup, OutlinedInput, Stack, Switch, Tab, Tabs, Typography } from '@mui/material';
4+
import SwapVert from '@mui/icons-material/SwapVert';
5+
import { Alert, AlertTitle, Badge, Box, Button, Checkbox, CircularProgress, Dialog, DialogContent, DialogTitle, Divider, FormControlLabel, FormGroup, IconButton, Menu, MenuItem, OutlinedInput, Stack, Switch, Tab, Tabs, Typography } from '@mui/material';
56
import React, { Suspense, useEffect, useState, useCallback, useMemo } from 'react';
7+
68
import { CATALOG_LAYOUT_SX } from '../Constants';
79
import { MCPClientState } from '../MCPClients';
810
import { CatalogItemRichened } from '../types/catalog';
@@ -43,6 +45,10 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
4345
const [tab, setTab] = useState<number>(0);
4446
const [ddVersion, setDdVersion] = useState<{ version: string, build: number } | null>(null);
4547
const [showMine, setShowMine] = useState<boolean>(localStorage.getItem('showMine') === 'true');
48+
const [openMenus, setOpenMenus] = useState<{ [key: string]: { anchorEl: HTMLElement | null, open: boolean } }>({
49+
'demo-customized-menu': { anchorEl: null, open: false }
50+
});
51+
const [sort, setSort] = useState<'name-asc' | 'name-desc' | 'date-desc'>('date-desc');
4652

4753
const loadDDVersion = useCallback(async () => {
4854
try {
@@ -134,12 +140,52 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
134140
<FormGroup>
135141
<Stack direction="row" spacing={1} alignItems='center' justifyContent="space-evenly">
136142
<OutlinedInput size="small" type="search" placeholder="Search" sx={{ width: 380 }} value={search} onChange={(e) => setSearch(e.target.value)} />
143+
<IconButton
144+
size="small"
145+
id="demo-customized-button"
146+
aria-controls={openMenus['demo-customized-menu'] ? 'demo-customized-menu' : undefined}
147+
aria-haspopup="true"
148+
aria-expanded={openMenus['demo-customized-menu'] ? 'true' : undefined}
149+
onClick={(e) => setOpenMenus({ ...openMenus, 'demo-customized-menu': { anchorEl: e.currentTarget, open: !openMenus['demo-customized-menu'].open } })}
150+
>
151+
<SwapVert fontSize="small" />
152+
</IconButton>
137153
<FormControlLabel control={<Switch checked={showMine} onChange={(e) => {
138154
setShowMine(e.target.checked)
139155
localStorage.setItem('showMine', e.target.checked.toString())
140156
}} />} label="Show only enabled tools" />
141157
</Stack>
142158
</FormGroup>
159+
160+
<Menu
161+
id="demo-customized-menu"
162+
MenuListProps={{
163+
'aria-labelledby': 'demo-customized-button',
164+
}}
165+
anchorEl={openMenus['demo-customized-menu'].anchorEl || undefined}
166+
open={openMenus['demo-customized-menu'].open}
167+
onClose={() => setOpenMenus({ ...openMenus, 'demo-customized-menu': { anchorEl: null, open: false } })}
168+
>
169+
<MenuItem sx={{ fontWeight: sort === 'date-desc' ? 'bold' : 'normal' }} onClick={() => {
170+
setOpenMenus({ ...openMenus, 'demo-customized-menu': { anchorEl: null, open: false } })
171+
setSort('date-desc')
172+
}} disableRipple>
173+
⏰ Most Recent
174+
</MenuItem>
175+
<Divider sx={{ my: 0.5 }} />
176+
<MenuItem sx={{ fontWeight: sort === 'name-asc' ? 'bold' : 'normal' }} onClick={() => {
177+
setOpenMenus({ ...openMenus, 'demo-customized-menu': { anchorEl: null, open: false } })
178+
setSort('name-asc')
179+
}} disableRipple>
180+
Name (A-Z)
181+
</MenuItem>
182+
<MenuItem sx={{ fontWeight: sort === 'name-desc' ? 'bold' : 'normal' }} onClick={() => {
183+
setOpenMenus({ ...openMenus, 'demo-customized-menu': { anchorEl: null, open: false } })
184+
setSort('name-desc')
185+
}} disableRipple>
186+
Name (Z-A)
187+
</MenuItem>
188+
</Menu>
143189
</Stack>
144190
}
145191

@@ -150,6 +196,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
150196
search={search}
151197
showMine={showMine}
152198
client={client}
199+
sort={sort}
153200
/>
154201
)}
155202
{tab === 1 && (
Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,63 @@
11
import React, { useMemo } from 'react';
22
import { Grid2 } from '@mui/material';
33
import Tile from '../tile/Index';
4-
import { v1 } from "@docker/extension-api-client-types";
4+
import { v1 } from '@docker/extension-api-client-types';
55
import { CATALOG_LAYOUT_SX } from '../../Constants';
66
import { useCatalogAll } from '../../queries/useCatalog';
77

88
interface ToolCatalogProps {
9-
search: string;
10-
client: v1.DockerDesktopClient;
11-
showMine: boolean;
9+
search: string;
10+
client: v1.DockerDesktopClient;
11+
showMine: boolean;
12+
sort: 'name-asc' | 'name-desc' | 'date-desc';
1213
}
1314

14-
const ToolCatalog: React.FC<ToolCatalogProps> = ({ search, client, showMine }) => {
15-
const { catalogItems, registryLoading } = useCatalogAll(client)
15+
const ToolCatalog: React.FC<ToolCatalogProps> = ({
16+
search,
17+
client,
18+
showMine,
19+
sort,
20+
}) => {
21+
const { catalogItems, registryLoading } = useCatalogAll(client);
1622

17-
// Memoize the filtered catalog items to prevent unnecessary recalculations
18-
const filteredCatalogItems = useMemo(() => {
19-
return catalogItems.filter(item => {
20-
const matchesSearch = item.name.toLowerCase().includes(search.toLowerCase());
21-
const hideBecauseItsNotMine = showMine && !item.registered;
22-
return matchesSearch && !hideBecauseItsNotMine;
23-
});
24-
}, [catalogItems, search, showMine]);
23+
// Memoize the filtered catalog items to prevent unnecessary recalculations
24+
const result = useMemo(() => {
25+
const filteredItems = catalogItems.filter((item) => {
26+
const matchesSearch = item.name
27+
.toLowerCase()
28+
.includes(search.toLowerCase());
29+
const hideBecauseItsNotMine = showMine && !item.registered;
30+
return matchesSearch && !hideBecauseItsNotMine;
31+
});
2532

26-
return (
27-
<Grid2 container spacing={1} sx={CATALOG_LAYOUT_SX}>
28-
{filteredCatalogItems.map((catalogItem) => {
29-
return (
30-
<Grid2 size={{ xs: 12, sm: 6, md: 4 }} key={catalogItem.name}>
31-
<Tile
32-
item={catalogItem}
33-
client={client}
34-
registryLoading={registryLoading}
35-
/>
36-
</Grid2>
37-
)
38-
})}
39-
</Grid2>
40-
);
33+
return sort !== 'date-desc'
34+
? filteredItems.sort((a, b) => {
35+
if (sort === 'name-asc') {
36+
return a.name.localeCompare(b.name);
37+
}
38+
if (sort === 'name-desc') {
39+
return b.name.localeCompare(a.name);
40+
}
41+
return 0;
42+
})
43+
: filteredItems;
44+
}, [catalogItems, search, showMine, sort]);
45+
46+
return (
47+
<Grid2 container spacing={1} sx={CATALOG_LAYOUT_SX}>
48+
{result.map((catalogItem) => {
49+
return (
50+
<Grid2 size={{ xs: 12, sm: 6, md: 4 }} key={catalogItem.name}>
51+
<Tile
52+
item={catalogItem}
53+
client={client}
54+
registryLoading={registryLoading}
55+
/>
56+
</Grid2>
57+
);
58+
})}
59+
</Grid2>
60+
);
4161
};
4262

43-
export default React.memo(ToolCatalog);
63+
export default React.memo(ToolCatalog);

0 commit comments

Comments
 (0)