mirror of
				https://github.com/walkxcode/dashboard-icons.git
				synced 2025-10-31 08:47:57 +01:00 
			
		
		
		
	fix(web): Run Biome checks and apply fixes
This commit is contained in:
		| @@ -1,13 +1,13 @@ | ||||
| "use client" | ||||
|  | ||||
| import { Badge } from "@/components/ui/badge" | ||||
| import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command" | ||||
| import { useMediaQuery } from "@/hooks/use-media-query" | ||||
| import { formatIconName, fuzzySearch, filterAndSortIcons } from "@/lib/utils" | ||||
| import { useRouter } from "next/navigation" | ||||
| import { useCallback, useEffect, useState, useMemo } from "react" | ||||
| import { filterAndSortIcons, formatIconName, fuzzySearch } from "@/lib/utils" | ||||
| import type { IconWithName } from "@/types/icons" | ||||
| import { Tag, Search as SearchIcon, Info } from "lucide-react" | ||||
| import { Badge } from "@/components/ui/badge" | ||||
| import { Info, Search as SearchIcon, Tag } from "lucide-react" | ||||
| import { useRouter } from "next/navigation" | ||||
| import { useCallback, useEffect, useMemo, useState } from "react" | ||||
|  | ||||
| interface CommandMenuProps { | ||||
| 	icons: IconWithName[] | ||||
| @@ -37,10 +37,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 		[externalOnOpenChange], | ||||
| 	) | ||||
|  | ||||
| 	const filteredIcons = useMemo(() => | ||||
| 		filterAndSortIcons({ icons, query, limit: 20 }), | ||||
| 		[icons, query] | ||||
| 	) | ||||
| 	const filteredIcons = useMemo(() => filterAndSortIcons({ icons, query, limit: 20 }), [icons, query]) | ||||
|  | ||||
| 	const totalIcons = icons.length | ||||
|  | ||||
| @@ -70,11 +67,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 	} | ||||
|  | ||||
| 	return ( | ||||
| 		<CommandDialog | ||||
| 			open={isOpen} | ||||
| 			onOpenChange={setIsOpen} | ||||
| 			contentClassName="bg-background/90 backdrop-blur-sm border border-border/60" | ||||
| 		> | ||||
| 		<CommandDialog open={isOpen} onOpenChange={setIsOpen} contentClassName="bg-background/90 backdrop-blur-sm border border-border/60"> | ||||
| 			<CommandInput | ||||
| 				placeholder={`Search our collection of ${totalIcons} icons by name or category...`} | ||||
| 				value={query} | ||||
| @@ -83,7 +76,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 			<CommandList className="max-h-[300px]"> | ||||
| 				{/* Icon Results */} | ||||
| 				<CommandGroup heading="Icons"> | ||||
| 					{filteredIcons.length > 0 && ( | ||||
| 					{filteredIcons.length > 0 && | ||||
| 						filteredIcons.map(({ name, data }) => { | ||||
| 							const formatedIconName = formatIconName(name) | ||||
| 							const hasCategories = data.categories && data.categories.length > 0 | ||||
| @@ -97,7 +90,9 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 								> | ||||
| 									<div className="flex-shrink-0 h-5 w-5 relative"> | ||||
| 										<div className="h-full w-full bg-primary/10 dark:bg-primary/20 rounded-md flex items-center justify-center"> | ||||
| 											<span className="text-[9px] font-medium text-primary dark:text-primary-foreground">{name.substring(0, 2).toUpperCase()}</span> | ||||
| 											<span className="text-[9px] font-medium text-primary dark:text-primary-foreground"> | ||||
| 												{name.substring(0, 2).toUpperCase()} | ||||
| 											</span> | ||||
| 										</div> | ||||
| 									</div> | ||||
| 									<span className="flex-grow capitalize font-medium text-sm">{formatedIconName}</span> | ||||
| @@ -110,9 +105,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 												className="text-xs font-normal inline-flex items-center gap-1 whitespace-nowrap max-w-[120px] overflow-hidden" | ||||
| 											> | ||||
| 												<Tag size={8} className="mr-1 flex-shrink-0" /> | ||||
| 												<span className="truncate"> | ||||
| 													{data.categories[0].replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())} | ||||
| 												</span> | ||||
| 												<span className="truncate">{data.categories[0].replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}</span> | ||||
| 											</Badge> | ||||
| 											{/* "+N" badge if more than one category */} | ||||
| 											{data.categories.length > 1 && ( | ||||
| @@ -124,8 +117,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 									)} | ||||
| 								</CommandItem> | ||||
| 							) | ||||
| 						}) | ||||
| 					)} | ||||
| 						})} | ||||
| 				</CommandGroup> | ||||
| 				<CommandEmpty> | ||||
| 					{/* Minimal empty state */} | ||||
| @@ -138,12 +130,10 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
|  | ||||
| 			{/* Separator and Browse section - Styled div outside CommandList */} | ||||
| 			<div className="border-t border-border/40 pt-1 mt-1 px-1 pb-1"> | ||||
| 				<div | ||||
| 					role="button" | ||||
| 					tabIndex={0} | ||||
| 					className="flex items-center gap-2 cursor-pointer rounded-sm px-2 py-1 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground" | ||||
| 				<button | ||||
| 					type="button" | ||||
| 					className="flex items-center gap-2 cursor-pointer rounded-sm px-2 py-1 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground w-full" | ||||
| 					onClick={handleBrowseAll} | ||||
| 					onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') handleBrowseAll() }} | ||||
| 				> | ||||
| 					<div className="flex-shrink-0 h-5 w-5 relative"> | ||||
| 						<div className="h-full w-full bg-primary/80 dark:bg-primary/40 rounded-md flex items-center justify-center"> | ||||
| @@ -151,7 +141,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO | ||||
| 						</div> | ||||
| 					</div> | ||||
| 					<span className="flex-grow text-sm">Browse all icons – {totalIcons} available</span> | ||||
| 				</div> | ||||
| 				</button> | ||||
| 			</div> | ||||
| 		</CommandDialog> | ||||
| 	) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ import { formatIconName } from "@/lib/utils" | ||||
| import type { AuthorData, Icon, IconFile } from "@/types/icons" | ||||
| import confetti from "canvas-confetti" | ||||
| import { motion } from "framer-motion" | ||||
| import { Check, Copy, Download, FileType, Github, Moon, PaletteIcon, Sun, ArrowRight } from "lucide-react" | ||||
| import { ArrowRight, Check, Copy, Download, FileType, Github, Moon, PaletteIcon, Sun } from "lucide-react" | ||||
| import dynamic from "next/dynamic" | ||||
| import Image from "next/image" | ||||
| import Link from "next/link" | ||||
| @@ -479,57 +479,63 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail | ||||
| 					</Card> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 			{iconData.categories && iconData.categories.length > 0 && (() => { | ||||
| 				const MAX_RELATED_ICONS = 16 | ||||
| 				const currentCategories = iconData.categories || [] | ||||
| 			{iconData.categories && | ||||
| 				iconData.categories.length > 0 && | ||||
| 				(() => { | ||||
| 					const MAX_RELATED_ICONS = 16 | ||||
| 					const currentCategories = iconData.categories || [] | ||||
|  | ||||
| 				const relatedIconsWithScore = Object.entries(allIcons) | ||||
| 					.map(([name, data]) => { | ||||
| 						if (name === icon) return null // Exclude the current icon | ||||
| 					const relatedIconsWithScore = Object.entries(allIcons) | ||||
| 						.map(([name, data]) => { | ||||
| 							if (name === icon) return null // Exclude the current icon | ||||
|  | ||||
| 						const otherCategories = data.categories || [] | ||||
| 						const commonCategories = currentCategories.filter((cat) => otherCategories.includes(cat)) | ||||
| 						const score = commonCategories.length | ||||
| 							const otherCategories = data.categories || [] | ||||
| 							const commonCategories = currentCategories.filter((cat) => otherCategories.includes(cat)) | ||||
| 							const score = commonCategories.length | ||||
|  | ||||
| 						return score > 0 ? { name, data, score } : null | ||||
| 					}) | ||||
| 					.filter((item): item is { name: string; data: Icon; score: number } => item !== null) // Type guard | ||||
| 					.sort((a, b) => b.score - a.score) // Sort by score DESC | ||||
| 							return score > 0 ? { name, data, score } : null | ||||
| 						}) | ||||
| 						.filter((item): item is { name: string; data: Icon; score: number } => item !== null) // Type guard | ||||
| 						.sort((a, b) => b.score - a.score) // Sort by score DESC | ||||
|  | ||||
| 				const topRelatedIcons = relatedIconsWithScore.slice(0, MAX_RELATED_ICONS) | ||||
| 					const topRelatedIcons = relatedIconsWithScore.slice(0, MAX_RELATED_ICONS) | ||||
|  | ||||
| 				const viewMoreUrl = `/icons?${currentCategories.map((cat) => `category=${encodeURIComponent(cat)}`).join("&")}` | ||||
| 					const viewMoreUrl = `/icons?${currentCategories.map((cat) => `category=${encodeURIComponent(cat)}`).join("&")}` | ||||
|  | ||||
| 				if (topRelatedIcons.length === 0) return null | ||||
| 					if (topRelatedIcons.length === 0) return null | ||||
|  | ||||
| 				return ( | ||||
| 					<section className="container mx-auto mt-12" aria-labelledby="related-icons-title"> | ||||
| 						<Card className="bg-background/50 border shadow-lg"> | ||||
| 							<CardHeader> | ||||
| 								<CardTitle> | ||||
| 									<h2 id="related-icons-title">Related Icons</h2> | ||||
| 								</CardTitle> | ||||
| 								<CardDescription> | ||||
| 									Other icons from {currentCategories.map((cat) => cat.replace(/-/g, " ")).join(", ")} categories | ||||
| 								</CardDescription> | ||||
| 							</CardHeader> | ||||
| 							<CardContent> | ||||
| 								<IconsGrid filteredIcons={topRelatedIcons} matchedAliases={{}} /> | ||||
| 								{relatedIconsWithScore.length > MAX_RELATED_ICONS && ( | ||||
| 									<div className="mt-6 text-center"> | ||||
| 										<Button asChild variant="link" className="text-muted-foreground hover:text-primary transition-colors duration-200 hover:no-underline"> | ||||
| 											<Link href={viewMoreUrl} className="no-underline"> | ||||
| 												View all related icons | ||||
| 												<ArrowRight className="ml-2 h-4 w-4" /> | ||||
| 											</Link> | ||||
| 										</Button> | ||||
| 									</div> | ||||
| 								)} | ||||
| 							</CardContent> | ||||
| 						</Card> | ||||
| 					</section> | ||||
| 				) | ||||
| 			})()} | ||||
| 					return ( | ||||
| 						<section className="container mx-auto mt-12" aria-labelledby="related-icons-title"> | ||||
| 							<Card className="bg-background/50 border shadow-lg"> | ||||
| 								<CardHeader> | ||||
| 									<CardTitle> | ||||
| 										<h2 id="related-icons-title">Related Icons</h2> | ||||
| 									</CardTitle> | ||||
| 									<CardDescription> | ||||
| 										Other icons from {currentCategories.map((cat) => cat.replace(/-/g, " ")).join(", ")} categories | ||||
| 									</CardDescription> | ||||
| 								</CardHeader> | ||||
| 								<CardContent> | ||||
| 									<IconsGrid filteredIcons={topRelatedIcons} matchedAliases={{}} /> | ||||
| 									{relatedIconsWithScore.length > MAX_RELATED_ICONS && ( | ||||
| 										<div className="mt-6 text-center"> | ||||
| 											<Button | ||||
| 												asChild | ||||
| 												variant="link" | ||||
| 												className="text-muted-foreground hover:text-primary transition-colors duration-200 hover:no-underline" | ||||
| 											> | ||||
| 												<Link href={viewMoreUrl} className="no-underline"> | ||||
| 													View all related icons | ||||
| 													<ArrowRight className="ml-2 h-4 w-4" /> | ||||
| 												</Link> | ||||
| 											</Button> | ||||
| 										</div> | ||||
| 									)} | ||||
| 								</CardContent> | ||||
| 							</Card> | ||||
| 						</section> | ||||
| 					) | ||||
| 				})()} | ||||
| 		</main> | ||||
| 	) | ||||
| } | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import { | ||||
| } from "@/components/ui/dropdown-menu" | ||||
| import { Input } from "@/components/ui/input" | ||||
| import { Separator } from "@/components/ui/separator" | ||||
| import { type SortOption, filterAndSortIcons } from "@/lib/utils" | ||||
| import type { IconSearchProps } from "@/types/icons" | ||||
| import { ArrowDownAZ, ArrowUpZA, Calendar, Filter, Search, SortAsc, X } from "lucide-react" | ||||
| import { useTheme } from "next-themes" | ||||
| @@ -24,7 +25,6 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation" | ||||
| import posthog from "posthog-js" | ||||
| import { useCallback, useEffect, useMemo, useRef, useState } from "react" | ||||
| import { toast } from "sonner" | ||||
| import { filterAndSortIcons, SortOption } from "@/lib/utils" | ||||
|  | ||||
| export function IconSearch({ icons }: IconSearchProps) { | ||||
| 	const searchParams = useSearchParams() | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import type { IconWithName } from "@/types/icons" | ||||
| import { type ClassValue, clsx } from "clsx" | ||||
| import { twMerge } from "tailwind-merge" | ||||
| import type { IconWithName } from "@/types/icons" | ||||
|  | ||||
| export function cn(...inputs: ClassValue[]) { | ||||
| 	return twMerge(clsx(inputs)) | ||||
| @@ -173,41 +173,40 @@ export function filterAndSortIcons({ | ||||
| 	// Filter by categories if any are selected | ||||
| 	if (categories.length > 0) { | ||||
| 		filtered = filtered.filter(({ data }) => | ||||
| 			data.categories.some((cat) => | ||||
| 				categories.some((selectedCat) => cat.toLowerCase() === selectedCat.toLowerCase()), | ||||
| 			), | ||||
| 			data.categories.some((cat) => categories.some((selectedCat) => cat.toLowerCase() === selectedCat.toLowerCase())), | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	if (query.trim()) { | ||||
| 		const queryWords = query.toLowerCase().split(/\s+/) | ||||
| 		const scored = filtered.map((icon) => { | ||||
| 			const nameScore = fuzzySearch(icon.name, query) * NAME_WEIGHT | ||||
| 			const aliasScore = | ||||
| 				icon.data.aliases && icon.data.aliases.length > 0 | ||||
| 					? Math.max(...icon.data.aliases.map((alias) => fuzzySearch(alias, query))) * ALIAS_WEIGHT | ||||
| 					: 0 | ||||
| 			const categoryScore = | ||||
| 				icon.data.categories && icon.data.categories.length > 0 | ||||
| 					? Math.max(...icon.data.categories.map((category) => fuzzySearch(category, query))) * CATEGORY_WEIGHT | ||||
| 					: 0 | ||||
| 		const scored = filtered | ||||
| 			.map((icon) => { | ||||
| 				const nameScore = fuzzySearch(icon.name, query) * NAME_WEIGHT | ||||
| 				const aliasScore = | ||||
| 					icon.data.aliases && icon.data.aliases.length > 0 | ||||
| 						? Math.max(...icon.data.aliases.map((alias) => fuzzySearch(alias, query))) * ALIAS_WEIGHT | ||||
| 						: 0 | ||||
| 				const categoryScore = | ||||
| 					icon.data.categories && icon.data.categories.length > 0 | ||||
| 						? Math.max(...icon.data.categories.map((category) => fuzzySearch(category, query))) * CATEGORY_WEIGHT | ||||
| 						: 0 | ||||
|  | ||||
| 			const maxScore = Math.max(nameScore, aliasScore, categoryScore) | ||||
| 				const maxScore = Math.max(nameScore, aliasScore, categoryScore) | ||||
|  | ||||
| 			// Penalize if only category matches | ||||
| 			const onlyCategoryMatch = | ||||
| 				categoryScore > 0.7 && nameScore < 0.5 && aliasScore < 0.5 | ||||
| 			const finalScore = onlyCategoryMatch ? maxScore * CATEGORY_PENALTY : maxScore | ||||
| 				// Penalize if only category matches | ||||
| 				const onlyCategoryMatch = categoryScore > 0.7 && nameScore < 0.5 && aliasScore < 0.5 | ||||
| 				const finalScore = onlyCategoryMatch ? maxScore * CATEGORY_PENALTY : maxScore | ||||
|  | ||||
| 			// Require all query words to be present in at least one field | ||||
| 			const allWordsPresent = queryWords.every((word) => | ||||
| 				icon.name.toLowerCase().includes(word) || | ||||
| 				icon.data.aliases.some((alias) => alias.toLowerCase().includes(word)) || | ||||
| 				icon.data.categories.some((cat) => cat.toLowerCase().includes(word)) | ||||
| 			) | ||||
| 				// Require all query words to be present in at least one field | ||||
| 				const allWordsPresent = queryWords.every( | ||||
| 					(word) => | ||||
| 						icon.name.toLowerCase().includes(word) || | ||||
| 						icon.data.aliases.some((alias) => alias.toLowerCase().includes(word)) || | ||||
| 						icon.data.categories.some((cat) => cat.toLowerCase().includes(word)), | ||||
| 				) | ||||
|  | ||||
| 			return { icon, score: allWordsPresent ? finalScore : finalScore * 0.4 } | ||||
| 		}) | ||||
| 				return { icon, score: allWordsPresent ? finalScore : finalScore * 0.4 } | ||||
| 			}) | ||||
| 			.filter((item) => item.score > 0.7) | ||||
| 			.sort((a, b) => { | ||||
| 				if (b.score !== a.score) return b.score - a.score | ||||
|   | ||||
		Reference in New Issue
	
	Block a user