Merge pull request #163 from y1hao/grouping
Some checks failed
CI / test-and-build (push) Has been cancelled
CI / Playwright Tests (push) Has been cancelled
CI / Build and Push Multi-Platform Docker Image (push) Has been cancelled
CI / deploy (push) Has been cancelled

feat: group autocomplete results by categories for better discoverability
This commit is contained in:
Ibrahima G. Coulibaly 2025-07-07 14:19:23 +01:00 committed by GitHub
commit 816a098971
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 84 additions and 35 deletions

View file

@ -1,4 +1,13 @@
import { Autocomplete, Box, Stack, TextField, useTheme } from '@mui/material';
import {
Autocomplete,
Box,
darken,
lighten,
Stack,
styled,
TextField,
useTheme
} from '@mui/material';
import Typography from '@mui/material/Typography';
import SearchIcon from '@mui/icons-material/Search';
import Grid from '@mui/material/Grid';
@ -8,7 +17,22 @@ import { filterTools, tools } from '@tools/index';
import { useNavigate } from 'react-router-dom';
import _ from 'lodash';
import { Icon } from '@iconify/react';
import { getToolCategoryTitle } from '@utils/string';
const GroupHeader = styled('div')(({ theme }) => ({
position: 'sticky',
top: '-8px',
padding: '4px 10px',
color: theme.palette.primary.main,
backgroundColor: lighten(theme.palette.primary.light, 0.85),
...theme.applyStyles('dark', {
backgroundColor: darken(theme.palette.primary.main, 0.8)
})
}));
const GroupItems = styled('ul')({
padding: 0
});
const exampleTools: { label: string; url: string }[] = [
{
label: 'Create a transparent image',
@ -26,9 +50,7 @@ const exampleTools: { label: string; url: string }[] = [
export default function Hero() {
const [inputValue, setInputValue] = useState<string>('');
const theme = useTheme();
const [filteredTools, setFilteredTools] = useState<DefinedTool[]>(
_.shuffle(tools)
);
const [filteredTools, setFilteredTools] = useState<DefinedTool[]>(tools);
const navigate = useNavigate();
const handleInputChange = (
event: React.ChangeEvent<{}>,
@ -66,6 +88,15 @@ export default function Hero() {
sx={{ mb: 2 }}
autoHighlight
options={filteredTools}
groupBy={(option) => option.type}
renderGroup={(params) => {
return (
<li key={params.key}>
<GroupHeader>{getToolCategoryTitle(params.group)}</GroupHeader>
<GroupItems>{params.children}</GroupItems>
</li>
);
}}
inputValue={inputValue}
getOptionLabel={(option) => option.name}
renderInput={(params) => (

View file

@ -4,7 +4,7 @@ import Typography from '@mui/material/Typography';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { filterTools, getToolsByCategory } from '../../tools';
import Hero from 'components/Hero';
import { capitalizeFirstLetter } from '@utils/string';
import { capitalizeFirstLetter, getToolCategoryTitle } from '@utils/string';
import { Icon } from '@iconify/react';
import { categoriesColors } from 'config/uiConfig';
import React, { useEffect } from 'react';
@ -21,9 +21,7 @@ export default function ToolsByCategory() {
const mainContentRef = React.useRef<HTMLDivElement>(null);
const { categoryName } = useParams();
const [searchTerm, setSearchTerm] = React.useState<string>('');
const rawTitle = getToolsByCategory().find(
(category) => category.type === categoryName
)!.rawTitle;
const rawTitle = getToolCategoryTitle(categoryName as string);
useEffect(() => {
if (mainContentRef.current) {

View file

@ -1,4 +1,5 @@
import { UpdateField } from '@components/options/ToolOptions';
import { getToolsByCategory } from '@tools/index';
// Here starting the shared values for string manipulation.
@ -105,3 +106,7 @@ export function itemCounter(
}
return dict;
}
export const getToolCategoryTitle = (categoryName: string): string =>
getToolsByCategory().find((category) => category.type === categoryName)!
.rawTitle;