Skip to content

Commit 53809cb

Browse files
committed
Created custom hooks
1 parent 76f1c7d commit 53809cb

File tree

10 files changed

+207
-246
lines changed

10 files changed

+207
-246
lines changed

CreatingIcons.md

-106
This file was deleted.

README.md

+41-27
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
# React + TypeScript + Vite
2-
3-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4-
5-
Currently, two official plugins are available:
6-
7-
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9-
10-
## Expanding the ESLint configuration
11-
12-
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13-
14-
- Configure the top-level `parserOptions` property like this:
15-
16-
```js
17-
parserOptions: {
18-
ecmaVersion: 'latest',
19-
sourceType: 'module',
20-
project: ['./tsconfig.json', './tsconfig.node.json'],
21-
tsconfigRootDir: __dirname,
22-
},
23-
```
24-
25-
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
26-
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
27-
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
1+
<div align="center">
2+
<h1>Devicon UI</h1>
3+
<h4>Front end • Open source • UI/UX</h4>
4+
</div>
5+
<h1>Contents</h1>
6+
7+
- [Overview](#overview)
8+
- [Technologies](#technologies)
9+
- [Installation](#installation)
10+
- [Contributing](#contributing)
11+
12+
<h1 id="overview">Overview</h1>
13+
14+
A set of icons representing programming languages, designing, and development tools.
15+
16+
<h1>Technologies</h1>
17+
<div align="center" id="technologies">
18+
19+
<table>
20+
<tr>
21+
<td align='center'><img src="https://img.shields.io/badge/react-%23121011.svg?style=for-the-badge&logo=react" alt="React"/></td>
22+
<td align='center'><img src="https://img.shields.io/badge/react_query-%23121011.svg?style=for-the-badge&logo=react-query" alt="React Query"/></td>
23+
<td align='center'><img src="https://img.shields.io/badge/vite_test-%23121011.svg?style=for-the-badge&logo=vite" alt="Vite Test"/></td>
24+
<td align='center'><img src="https://img.shields.io/badge/storybook-%23121011.svg?style=for-the-badge&logo=storybook" alt="Storybook"/></td>
25+
<td align='center'><img src="https://img.shields.io/badge/tailwind-%23121011.svg?style=for-the-badge&logo=tailwind-css" alt="Tailwind CSS"/></td>
26+
<td align='center'><img src="https://img.shields.io/badge/zustand-%23121011.svg?style=for-the-badge&logo=zustand" alt="Zustand"/></td>
27+
</tr>
28+
</table>
29+
</div>
30+
31+
<h1>Installation</h1>
32+
33+
1. Install Node.js on your computer.
34+
2. Clone this repository to your local machine using `git clone`.
35+
3. Navigate to the project directory.
36+
4. Install the required dependencies using `npm install` or `yarn install`.
37+
38+
This command installs the necessary dependencies, including React, React Query, Vite Test, Storybook, Tailwind CSS, and Zustand.
39+
40+
<h1>Contributing</h1>
41+
At the moment, we are not accepting contributions. However, we appreciate your support and encourage you to stay updated with any future developments.

src/features/dashbord/filters/FilterList.tsx

+16-9
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { IIconFilter } from "./types";
1+
import { IIconFilter, IIconFilterGroup } from "./types";
22

33
interface FilterListProps {
44
title: string;
5-
filters: IIconFilter[];
5+
filterGroup: IIconFilterGroup;
66
iconMap: { [key: string]: string };
7-
limit?: number;
8-
handleFilter: (category: IIconFilter) => void;
7+
isLimited: boolean;
8+
handleFilter: (filter: IIconFilter) => void;
9+
resetFilters?: () => void;
910
}
1011

11-
export const FilterList = ({ title, filters, iconMap, limit, handleFilter }: FilterListProps) => {
12-
const selectedFiltersCount = filters.reduce((count, filter) => (filter.isSelected ? count + 1 : count), 0);
12+
export const FilterList = ({ title, filterGroup, iconMap, isLimited, handleFilter, resetFilters }: FilterListProps) => {
13+
const selectedFiltersCount = filterGroup.filters.reduce((count, filter) => (filter.isSelected ? count + 1 : count), 0);
1314
const isSelectedClass = (filter: IIconFilter) =>
1415
filter.isSelected
1516
? "bg-green-600 hover:bg-green-700 text-white dark:bg-zinc-900 dark:hover:bg-zinc-700 shadow-sm"
@@ -20,11 +21,17 @@ export const FilterList = ({ title, filters, iconMap, limit, handleFilter }: Fil
2021
<div className="flex flex-row gap-2 mb-2 dark:text-white">
2122
<p className="font-bold text-md ">{title}</p>
2223
<p className="text-sm my-auto">
23-
({selectedFiltersCount} / {filters.length})
24+
({selectedFiltersCount} / {filterGroup.filters.length})
2425
</p>
26+
27+
{resetFilters && (
28+
<button className="ml-auto text-sm text-green-600" onClick={resetFilters}>
29+
Reset
30+
</button>
31+
)}
2532
</div>
26-
<div className={`flex flex-col gap-2 overflow-y-auto ${limit ? 'h-96' : 'h-fit'} pr-2`}>
27-
{filters.map((filter, index) => (
33+
<div className={`flex flex-col gap-2 overflow-y-auto ${isLimited ? 'h-96' : 'h-fit'} pr-2`}>
34+
{filterGroup.filters.map((filter, index) => (
2835
<button
2936
key={index}
3037
className={`${isSelectedClass(filter)} rounded-md flex px-4 py-2 text-sm`}
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,58 @@
11
import { IIcon, IconVersion } from "../../../../types";
2-
import { IIconFilter } from "../types";
2+
import { FilterType, IIconFilter, IIconFilterGroup } from "../types";
3+
4+
const FilterMapRecord: Record<FilterType, string> = {
5+
versions: 'versions.svg',
6+
color: 'color',
7+
tags: 'tags',
8+
alias: 'alias',
9+
name: 'name'
10+
};
11+
12+
const getItemsFromPath = (icons: IIcon, path: string): string[] => {
13+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
14+
const items = path.split('.').reduce((acc: any, curr: string) => acc[curr], icons);
15+
return items;
16+
}
317

4-
export const populateIconFilters = (icons: IIcon[], filters: IIconFilter[], attribute: 'versions.svg' | 'tags') => {
5-
// Reset filters
6-
filters.forEach((filter: IIconFilter) => {
18+
export const populateIconFilters = (icons: IIcon[], filterGroup: IIconFilterGroup) => {
19+
const itemsPath = FilterMapRecord[filterGroup.filterType];
20+
filterGroup.filters.forEach(filter => {
721
filter.numberOfIcons = 0;
822
});
923

10-
// Populate filters
11-
icons.forEach((icon: IIcon) => {
12-
const items: string[] = attribute === 'versions.svg' ? icon.versions.svg : icon.tags;
13-
items.forEach((item: string) => {
14-
const category = filters.find(category => category.filterName === item);
24+
icons.forEach(icon => {
25+
const items: string[] = getItemsFromPath(icon, itemsPath);
26+
items.forEach(item => {
27+
const category = filterGroup.filters.find(category => category.filterName === item);
1528
if (category) {
1629
category.numberOfIcons++;
1730
} else {
18-
filters.push({ filterName: item as IconVersion, numberOfIcons: 1, isSelected: false });
31+
filterGroup.filters.push({ filterName: item as IconVersion, numberOfIcons: 1, isSelected: false });
1932
}
2033
});
2134
});
2235

23-
// Sort filters
24-
filters.sort((a, b) => b.numberOfIcons - a.numberOfIcons);
25-
26-
return filters;
36+
filterGroup.filters.sort((a, b) => b.numberOfIcons - a.numberOfIcons);
37+
return filterGroup;
2738
};
2839

40+
const FilterFunctions: Record<FilterType, (icon: IIcon, criterion: string | IconVersion) => boolean> = {
41+
name: (icon, name) => [icon.name, ...(icon.altnames ?? [])].some(n => n.toLowerCase().includes(name as string)),
42+
versions: (icon, version) => icon.versions.svg.includes(version as IconVersion),
43+
tags: (icon, tag) => icon.tags.includes(tag as string),
44+
color: () => true, // Placeholder function, update as required
45+
alias: () => true // Placeholder function, update as required
46+
};
2947

30-
export const filterIconsByName = (icons: IIcon[], search: string): IIcon[] => {
31-
search = search.trim().toLowerCase();
32-
return icons.filter(icon => {
33-
const names = [icon.name, ...(icon.altnames ?? [])];
34-
return names.some(name => name.toLowerCase().includes(search));
35-
});
36-
}
37-
38-
export const filterIconsByVersion = (icons: IIcon[], version: IconVersion): IIcon[] => {
39-
return icons.filter(icon => icon.versions.svg.includes(version));
40-
}
41-
42-
export const filterIconsByTag = (icons: IIcon[], tag: string): IIcon[] => {
43-
return icons.filter(icon => icon.tags.includes(tag));
48+
export const filterIcons = (icons: IIcon[], filterType: FilterType, criterion: string | IconVersion): IIcon[] => {
49+
return icons.filter(icon => FilterFunctions[filterType](icon, criterion));
4450
}
4551

46-
export const updateFilters = (filters: IIconFilter[], filter: IIconFilter) => {
47-
const updatedFilters = [...filters];
48-
const index = updatedFilters.findIndex((c) => c.filterName === filter.filterName);
49-
updatedFilters[index].isSelected = !updatedFilters[index].isSelected;
50-
return updatedFilters;
52+
export const updateFilter = (filterGroup: IIconFilterGroup, filter: IIconFilter) => {
53+
const tempFilterGroup = { ...filterGroup };
54+
const filterIndex = tempFilterGroup.filters.findIndex(item => item.filterName === filter.filterName);
55+
if (filterIndex === -1) return filterGroup;
56+
tempFilterGroup.filters[filterIndex].isSelected = !filter.isSelected;
57+
return tempFilterGroup;
5158
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useState, useEffect } from 'react';
2+
import { IIcon, IconVersion } from '../../../../types';
3+
import { IIconFilterGroup } from '../types';
4+
import { filterIcons, populateIconFilters } from '../helpers';
5+
6+
const INIT_FILTER_GROUPS: IIconFilterGroup[] = [
7+
{
8+
groupName: 'Versions',
9+
filterType: 'versions',
10+
filters: [],
11+
},
12+
{
13+
groupName: 'Tags',
14+
filterType: 'tags',
15+
filters: [],
16+
}
17+
]
18+
19+
20+
export const useFilterGroups = (icons: IIcon[], initialFilterGroups: IIconFilterGroup[] = INIT_FILTER_GROUPS) => {
21+
const [filterGroups, setFilterGroups] = useState<IIconFilterGroup[]>(initialFilterGroups);
22+
23+
useEffect(() => {
24+
if (!icons || icons.length === 0) return;
25+
setFilterGroups(prevGroups => prevGroups.map(group => populateIconFilters(icons, group)));
26+
}, [icons]);
27+
return { filterGroups, setFilterGroups };
28+
};
29+
30+
31+
32+
export const useFilteredIcons = (icons: IIcon[], filterGroups: IIconFilterGroup[], searchTerm: string) => {
33+
const [filteredIcons, setFilteredIcons] = useState<IIcon[]>(icons);
34+
35+
useEffect(() => {
36+
let filtered = searchTerm ? filterIcons(icons, 'name', searchTerm) : icons;
37+
filterGroups.forEach(group => {
38+
group.filters.forEach(filter => {
39+
if (filter.isSelected) {
40+
filtered = filterIcons(filtered, group.filterType, filter.filterName as IconVersion);
41+
}
42+
});
43+
});
44+
setFilteredIcons(filtered);
45+
}, [icons, filterGroups, searchTerm]);
46+
47+
return filteredIcons;
48+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './FilterHooks'
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11

2+
export type FilterType = 'versions' | 'color' | 'tags' | 'alias' | 'name'
3+
24
export interface IIconFilter {
35
filterName: string,
46
numberOfIcons: number,
57
isSelected: boolean
8+
}
9+
10+
export interface IIconFilterGroup {
11+
groupName: string,
12+
filterType: FilterType,
13+
filters: IIconFilter[]
614
}

0 commit comments

Comments
 (0)