mirror of
https://github.com/walkxcode/dashboard-icons.git
synced 2025-10-27 05:29:03 +08:00
chore: format codebase
This commit is contained in:
@@ -7,10 +7,7 @@
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"includes": [
|
||||
"src/**",
|
||||
"!src/components/ui"
|
||||
]
|
||||
"includes": ["src/**", "!src/components/ui"]
|
||||
},
|
||||
"formatter": {
|
||||
"lineWidth": 140,
|
||||
@@ -22,6 +19,8 @@
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off",
|
||||
"noUnknownAtRules": "off",
|
||||
"noArrayIndexKey": "off"
|
||||
}
|
||||
}
|
||||
@@ -39,4 +38,4 @@
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { AlertTriangle, ArrowLeft, RefreshCcw } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useEffect } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
|
||||
export default function ErrorPage({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
export default function ErrorPage({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -125,20 +125,20 @@
|
||||
|
||||
--background: oklch(0.99 0 0);
|
||||
--foreground: oklch(0.32 0 0);
|
||||
--card: oklch(1.0 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.32 0 0);
|
||||
--popover: oklch(1.0 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.32 0 0);
|
||||
--primary: oklch(0.67 0.2 23.8);
|
||||
--primary-foreground: oklch(1.0 0 0);
|
||||
--secondary: oklch(0.97 0.0 264.54);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.97 0 264.54);
|
||||
--secondary-foreground: oklch(0.45 0.03 256.8);
|
||||
--muted: oklch(0.98 0.0 247.84);
|
||||
--muted: oklch(0.98 0 247.84);
|
||||
--muted-foreground: oklch(0.55 0.02 264.36);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.64 0.21 25.33);
|
||||
--destructive-foreground: oklch(1.0 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.9 0.01 247.88);
|
||||
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
@@ -159,16 +159,11 @@
|
||||
|
||||
--shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
|
||||
--magic-gradient-color: oklch(0.67 0.2 23.8 / 15%);
|
||||
@@ -182,7 +177,7 @@
|
||||
--popover: oklch(0.29 0.02 268.4);
|
||||
--popover-foreground: oklch(0.92 0 0);
|
||||
--primary: oklch(0.67 0.2 23.8);
|
||||
--primary-foreground: oklch(1.0 0 0);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.31 0.03 266.71);
|
||||
--secondary-foreground: oklch(0.92 0 0);
|
||||
--muted: oklch(0.31 0.03 266.71);
|
||||
@@ -190,7 +185,7 @@
|
||||
--accent: oklch(0.34 0.06 267.59);
|
||||
--accent-foreground: oklch(0.88 0.06 254.13);
|
||||
--destructive: oklch(0.64 0.21 25.33);
|
||||
--destructive-foreground: oklch(1.0 0 0);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.38 0.03 269.73);
|
||||
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
@@ -211,16 +206,11 @@
|
||||
|
||||
--shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px
|
||||
hsl(0 0% 0% / 0.1);
|
||||
--shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px hsl(0 0% 0% / 0.1);
|
||||
--shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
|
||||
--magic-gradient-color: oklch(0.27 0 0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readFile } from "node:fs/promises"
|
||||
import { join } from "node:path"
|
||||
import { getAllIcons } from "@/lib/api"
|
||||
import { ImageResponse } from "next/og"
|
||||
import { getAllIcons } from "@/lib/api"
|
||||
|
||||
export const dynamic = "force-static"
|
||||
|
||||
@@ -42,7 +42,7 @@ export default async function Image({ params }: { params: { icon: string } }) {
|
||||
const iconPath = join(process.cwd(), `../png/${icon}.png`)
|
||||
console.log(`Generating opengraph image for ${icon} (${index + 1} / ${totalIcons}) from path ${iconPath}`)
|
||||
iconData = await readFile(iconPath)
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
console.error(`Icon ${icon} was not found locally`)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Metadata, ResolvingMetadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
import { IconDetails } from "@/components/icon-details"
|
||||
import { BASE_URL, WEB_URL } from "@/constants"
|
||||
import { getAllIcons, getAuthorData } from "@/lib/api"
|
||||
import type { Metadata, ResolvingMetadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
export const dynamicParams = false
|
||||
|
||||
export async function generateStaticParams() {
|
||||
@@ -19,7 +19,7 @@ type Props = {
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params, searchParams }: Props, parent: ResolvingMetadata): Promise<Metadata> {
|
||||
export async function generateMetadata({ params, searchParams }: Props, _parent: ResolvingMetadata): Promise<Metadata> {
|
||||
const { icon } = await params
|
||||
const iconsData = await getAllIcons()
|
||||
if (!iconsData[icon]) {
|
||||
@@ -76,13 +76,14 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
|
||||
type: "website",
|
||||
url: pageUrl,
|
||||
siteName: "Dashboard Icons",
|
||||
images: [{
|
||||
url: `${BASE_URL}/webp/${icon}.webp`,
|
||||
width: 512,
|
||||
height: 512,
|
||||
alt: `${formattedIconName} icon`,
|
||||
}]
|
||||
|
||||
images: [
|
||||
{
|
||||
url: `${BASE_URL}/webp/${icon}.webp`,
|
||||
width: 512,
|
||||
height: 512,
|
||||
alt: `${formattedIconName} icon`,
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import type React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface BackgroundWrapperProps {
|
||||
children: React.ReactNode
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getAllIcons } from "@/lib/api"
|
||||
import { ImageResponse } from "next/og"
|
||||
import { getAllIcons } from "@/lib/api"
|
||||
|
||||
export const dynamic = "force-static"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Metadata } from "next"
|
||||
import { IconSearch } from "@/components/icon-search"
|
||||
import { BASE_URL } from "@/constants"
|
||||
import { getIconsArray } from "@/lib/api"
|
||||
import type { Metadata } from "next"
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const icons = await getIconsArray()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { PostHogProvider } from "@/components/PostHogProvider"
|
||||
import { Footer } from "@/components/footer"
|
||||
import { HeaderWrapper } from "@/components/header-wrapper"
|
||||
import { LicenseNotice } from "@/components/license-notice"
|
||||
import { BASE_URL, WEB_URL, getDescription, websiteTitle } from "@/constants"
|
||||
import { getTotalIcons } from "@/lib/api"
|
||||
import type { Metadata, Viewport } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import { Toaster } from "sonner"
|
||||
import { Footer } from "@/components/footer"
|
||||
import { HeaderWrapper } from "@/components/header-wrapper"
|
||||
import { LicenseNotice } from "@/components/license-notice"
|
||||
import { PostHogProvider } from "@/components/PostHogProvider"
|
||||
import { BASE_URL, getDescription, WEB_URL, websiteTitle } from "@/constants"
|
||||
import { getTotalIcons } from "@/lib/api"
|
||||
import "./globals.css"
|
||||
import { ThemeProvider } from "./theme-provider"
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { AlertTriangle, ArrowLeft } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { IconSubmissionContent } from "@/components/icon-submission-form"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { AlertTriangle, ArrowLeft, PlusCircle } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
|
||||
export default function NotFound({
|
||||
error,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
}) {
|
||||
export default function NotFound({ error }: { error: Error & { digest?: string } }) {
|
||||
return (
|
||||
<div className="py-16 flex items-center justify-center">
|
||||
<div className="text-center space-y-8 max-w-2xl mx-auto">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { MetadataRoute } from "next"
|
||||
import { BASE_URL, WEB_URL } from "@/constants"
|
||||
import { getAllIcons } from "@/lib/api"
|
||||
import type { MetadataRoute } from "next"
|
||||
|
||||
export const dynamic = "force-static"
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { useEffect, useRef } from "react"
|
||||
export function Carbon() {
|
||||
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
||||
const ref = useRef<HTMLDivElement>(null!)
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return null
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const serve = "CW7IKKQM"
|
||||
const placement = "dashboardiconscom"
|
||||
@@ -16,6 +10,11 @@ export function Carbon() {
|
||||
ref.current.appendChild(s)
|
||||
}, [])
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null!)
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>
|
||||
|
||||
@@ -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 { filterAndSortIcons, formatIconName, fuzzySearch } from "@/lib/utils"
|
||||
import type { IconWithName } from "@/types/icons"
|
||||
import { Info, Search as SearchIcon, Tag } from "lucide-react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
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 { filterAndSortIcons, formatIconName } from "@/lib/utils"
|
||||
import type { IconWithName } from "@/types/icons"
|
||||
|
||||
interface CommandMenuProps {
|
||||
icons: IconWithName[]
|
||||
@@ -20,7 +20,7 @@ export function CommandMenu({ icons, open: externalOpen, onOpenChange: externalO
|
||||
const router = useRouter()
|
||||
const [internalOpen, setInternalOpen] = useState(false)
|
||||
const [query, setQuery] = useState("")
|
||||
const isDesktop = useMediaQuery("(min-width: 768px)")
|
||||
const _isDesktop = useMediaQuery("(min-width: 768px)")
|
||||
|
||||
// Use either external or internal state for controlling open state
|
||||
const isOpen = externalOpen !== undefined ? externalOpen : internalOpen
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { REPO_PATH } from "@/constants"
|
||||
import { ExternalLink } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
import { HeartEasterEgg } from "./heart"
|
||||
|
||||
export function Footer() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export function HeaderNav() {
|
||||
const pathname = usePathname()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { Github, PlusCircle, Search } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useEffect, useState } from "react"
|
||||
import { IconSubmissionForm } from "@/components/icon-submission-form"
|
||||
import { ThemeSwitcher } from "@/components/theme-switcher"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
import { getIconsArray } from "@/lib/api"
|
||||
import type { IconWithName } from "@/types/icons"
|
||||
import { Github, PlusCircle, Search } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useEffect, useState } from "react"
|
||||
import { CommandMenu } from "./command-menu"
|
||||
import { HeaderNav } from "./header-nav"
|
||||
import { Button } from "./ui/button"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { Heart } from "lucide-react"
|
||||
|
||||
import { motion } from "framer-motion"
|
||||
import { Heart } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
|
||||
export function HeartEasterEgg() {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Separator } from "@radix-ui/react-dropdown-menu"
|
||||
import { motion, useAnimation, useInView } from "framer-motion"
|
||||
import {
|
||||
@@ -25,6 +21,10 @@ import {
|
||||
} from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { AuroraText } from "./magicui/aurora-text"
|
||||
import { InteractiveHoverButton } from "./magicui/interactive-hover-button"
|
||||
import { NumberTicker } from "./magicui/number-ticker"
|
||||
@@ -35,7 +35,7 @@ interface IconCardProps {
|
||||
imageUrl: string
|
||||
}
|
||||
|
||||
function IconCard({ name, imageUrl }: IconCardProps) {
|
||||
function _IconCard({ name, imageUrl }: IconCardProps) {
|
||||
return (
|
||||
<Card className="p-4 flex flex-col items-center gap-2 cursor-pointer group hover-lift card-hover">
|
||||
<div className="w-16 h-16 flex items-center justify-center">
|
||||
|
||||
@@ -1,122 +1,109 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Check, Copy, Download, Github, Link as LinkIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type React from "react";
|
||||
import { Check, Copy, Download, Github, Link as LinkIcon } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import type React from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
|
||||
export type IconActionsProps = {
|
||||
imageUrl: string;
|
||||
githubUrl: string;
|
||||
iconName: string;
|
||||
format: string;
|
||||
variantKey: string;
|
||||
copiedUrlKey: string | null;
|
||||
copiedImageKey: string | null;
|
||||
handleDownload: (event: React.MouseEvent, url: string, filename: string) => Promise<void>;
|
||||
handleCopyUrl: (url: string, variantKey: string, event?: React.MouseEvent) => void;
|
||||
handleCopyImage: (imageUrl: string, format: string, variantKey: string, event?: React.MouseEvent) => Promise<void>;
|
||||
};
|
||||
imageUrl: string
|
||||
githubUrl: string
|
||||
iconName: string
|
||||
format: string
|
||||
variantKey: string
|
||||
copiedUrlKey: string | null
|
||||
copiedImageKey: string | null
|
||||
handleDownload: (event: React.MouseEvent, url: string, filename: string) => Promise<void>
|
||||
handleCopyUrl: (url: string, variantKey: string, event?: React.MouseEvent) => void
|
||||
handleCopyImage: (imageUrl: string, format: string, variantKey: string, event?: React.MouseEvent) => Promise<void>
|
||||
}
|
||||
|
||||
export function IconActions({
|
||||
imageUrl,
|
||||
githubUrl,
|
||||
iconName,
|
||||
format,
|
||||
variantKey,
|
||||
copiedUrlKey,
|
||||
copiedImageKey,
|
||||
handleDownload,
|
||||
handleCopyUrl,
|
||||
handleCopyImage,
|
||||
imageUrl,
|
||||
githubUrl,
|
||||
iconName,
|
||||
format,
|
||||
variantKey,
|
||||
copiedUrlKey,
|
||||
copiedImageKey,
|
||||
handleDownload,
|
||||
handleCopyUrl,
|
||||
handleCopyImage,
|
||||
}: IconActionsProps) {
|
||||
const downloadFilename = `${iconName}.${format}`;
|
||||
const isUrlCopied = copiedUrlKey === variantKey;
|
||||
const isImageCopied = copiedImageKey === variantKey;
|
||||
const downloadFilename = `${iconName}.${format}`
|
||||
const isUrlCopied = copiedUrlKey === variantKey
|
||||
const isImageCopied = copiedImageKey === variantKey
|
||||
|
||||
return (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div className="flex gap-2 mt-3 w-full justify-center">
|
||||
{/* Download Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleDownload(e, imageUrl, downloadFilename)}
|
||||
aria-label={`Download ${iconName} as ${format.toUpperCase()}`}
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Download {format.toUpperCase()}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
return (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<div className="flex gap-2 mt-3 w-full justify-center">
|
||||
{/* Download Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleDownload(e, imageUrl, downloadFilename)}
|
||||
aria-label={`Download ${iconName} as ${format.toUpperCase()}`}
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Download {format.toUpperCase()}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* Copy Image Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyImage(imageUrl, format, variantKey, e)}
|
||||
aria-label={`Copy ${iconName} image as ${format.toUpperCase()}`}
|
||||
>
|
||||
{isImageCopied ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy image to clipboard</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{/* Copy Image Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyImage(imageUrl, format, variantKey, e)}
|
||||
aria-label={`Copy ${iconName} image as ${format.toUpperCase()}`}
|
||||
>
|
||||
{isImageCopied ? <Check className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy image to clipboard</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* Copy URL Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyUrl(imageUrl, variantKey, e)}
|
||||
aria-label={`Copy direct URL for ${iconName} ${format.toUpperCase()}`}
|
||||
>
|
||||
{isUrlCopied ? (
|
||||
<Check className="w-4 h-4 text-green-500" />
|
||||
) : (
|
||||
<LinkIcon className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy direct URL</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{/* Copy URL Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-lg cursor-pointer"
|
||||
onClick={(e) => handleCopyUrl(imageUrl, variantKey, e)}
|
||||
aria-label={`Copy direct URL for ${iconName} ${format.toUpperCase()}`}
|
||||
>
|
||||
{isUrlCopied ? <Check className="w-4 h-4 text-green-500" /> : <LinkIcon className="w-4 h-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy direct URL</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{/* View on GitHub Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="h-8 w-8 rounded-lg" asChild>
|
||||
<Link
|
||||
href={githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={`View ${iconName} ${format} file on GitHub`}
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>View on GitHub</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
{/* View on GitHub Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="outline" size="icon" className="h-8 w-8 rounded-lg" asChild>
|
||||
<Link href={githubUrl} target="_blank" rel="noopener noreferrer" aria-label={`View ${iconName} ${format} file on GitHub`}>
|
||||
<Github className="w-4 h-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>View on GitHub</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { MagicCard } from "@/components/magicui/magic-card"
|
||||
import { BASE_URL } from "@/constants"
|
||||
import { formatIconName } from "@/lib/utils"
|
||||
import type { Icon } from "@/types/icons"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
|
||||
export function IconCard({
|
||||
name,
|
||||
data: iconData,
|
||||
matchedAlias,
|
||||
}: {
|
||||
name: string
|
||||
data: Icon
|
||||
matchedAlias?: string
|
||||
}) {
|
||||
export function IconCard({ name, data: iconData, matchedAlias }: { name: string; data: Icon; matchedAlias?: string }) {
|
||||
const formatedIconName = formatIconName(name)
|
||||
return (
|
||||
<MagicCard className="rounded-md shadow-md">
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { IconsGrid } from "@/components/icon-grid"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { BASE_URL, REPO_PATH } from "@/constants"
|
||||
import { formatIconName } from "@/lib/utils"
|
||||
import type { AuthorData, Icon, IconFile } from "@/types/icons"
|
||||
import confetti from "canvas-confetti"
|
||||
import { motion } from "framer-motion"
|
||||
import { ArrowRight, Check, FileType, Github, Moon, PaletteIcon, Sun, Type } from "lucide-react"
|
||||
@@ -16,16 +8,20 @@ import Link from "next/link"
|
||||
import type React from "react"
|
||||
import { useCallback, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { IconsGrid } from "@/components/icon-grid"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { BASE_URL, REPO_PATH } from "@/constants"
|
||||
import { formatIconName } from "@/lib/utils"
|
||||
import type { AuthorData, Icon, IconFile } from "@/types/icons"
|
||||
import { Carbon } from "./carbon"
|
||||
import { IconActions } from "./icon-actions"
|
||||
import { MagicCard } from "./magicui/magic-card"
|
||||
import { Badge } from "./ui/badge"
|
||||
|
||||
type RenderVariantFn = (
|
||||
format: string,
|
||||
iconName: string,
|
||||
theme?: "light" | "dark"
|
||||
) => React.ReactNode
|
||||
type RenderVariantFn = (format: string, iconName: string, theme?: "light" | "dark") => React.ReactNode
|
||||
|
||||
type IconVariantsSectionProps = {
|
||||
title: string
|
||||
@@ -76,11 +72,7 @@ type WordmarkSectionProps = {
|
||||
renderVariant: RenderVariantFn
|
||||
}
|
||||
|
||||
function WordmarkSection({
|
||||
iconData,
|
||||
aavailableFormats,
|
||||
renderVariant,
|
||||
}: WordmarkSectionProps) {
|
||||
function WordmarkSection({ iconData, aavailableFormats, renderVariant }: WordmarkSectionProps) {
|
||||
if (!iconData.wordmark) return null
|
||||
|
||||
return (
|
||||
@@ -89,9 +81,7 @@ function WordmarkSection({
|
||||
<Type className="w-4 h-4 text-green-500" />
|
||||
Wordmark Variants
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
Icon variants that include the brand name. Click to copy URL.
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground mb-4">Icon variants that include the brand name. Click to copy URL.</p>
|
||||
<div className="space-y-6">
|
||||
{iconData.wordmark.light && (
|
||||
<div>
|
||||
@@ -135,14 +125,14 @@ export type IconDetailsProps = {
|
||||
|
||||
export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetailsProps) {
|
||||
const authorName = authorData.name || authorData.login || ""
|
||||
const iconColorVariants = iconData.colors
|
||||
const iconWordmarkVariants = iconData.wordmark
|
||||
const _iconColorVariants = iconData.colors
|
||||
const _iconWordmarkVariants = iconData.wordmark
|
||||
const formattedDate = new Date(iconData.update.timestamp).toLocaleDateString("en-GB", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})
|
||||
|
||||
|
||||
const getAvailableFormats = () => {
|
||||
switch (iconData.base) {
|
||||
case "svg":
|
||||
@@ -155,7 +145,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
}
|
||||
|
||||
const availableFormats = getAvailableFormats()
|
||||
const [copiedVariants, setCopiedVariants] = useState<Record<string, boolean>>({})
|
||||
const [copiedVariants, _setCopiedVariants] = useState<Record<string, boolean>>({})
|
||||
const [copiedUrlKey, setCopiedUrlKey] = useState<string | null>(null)
|
||||
const [copiedImageKey, setCopiedImageKey] = useState<string | null>(null)
|
||||
|
||||
@@ -207,16 +197,11 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
})
|
||||
}
|
||||
|
||||
const handleCopyImage = async (
|
||||
imageUrl: string,
|
||||
format: string,
|
||||
variantKey: string,
|
||||
event?: React.MouseEvent
|
||||
) => {
|
||||
const handleCopyImage = async (imageUrl: string, format: string, variantKey: string, event?: React.MouseEvent) => {
|
||||
try {
|
||||
toast.loading("Copying image...")
|
||||
|
||||
if (format === 'svg') {
|
||||
if (format === "svg") {
|
||||
const response = await fetch(imageUrl)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch SVG: ${response.statusText}`)
|
||||
@@ -240,8 +225,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
toast.success("SVG Markup Copied", {
|
||||
description: "The SVG code has been copied to your clipboard.",
|
||||
})
|
||||
|
||||
} else if (format === 'png' || format === 'webp') {
|
||||
} else if (format === "png" || format === "webp") {
|
||||
const mimeType = `image/${format}`
|
||||
const response = await fetch(imageUrl)
|
||||
if (!response.ok) {
|
||||
@@ -250,10 +234,10 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
const blob = await response.blob()
|
||||
|
||||
if (!blob) {
|
||||
throw new Error('Failed to generate image blob')
|
||||
throw new Error("Failed to generate image blob")
|
||||
}
|
||||
|
||||
await navigator.clipboard.write([new ClipboardItem({ [mimeType]: blob })]);
|
||||
await navigator.clipboard.write([new ClipboardItem({ [mimeType]: blob })])
|
||||
|
||||
setCopiedImageKey(variantKey)
|
||||
setTimeout(() => {
|
||||
@@ -270,11 +254,9 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
toast.success("Image copied", {
|
||||
description: `The ${format.toUpperCase()} image has been copied to your clipboard.`,
|
||||
})
|
||||
|
||||
} else {
|
||||
throw new Error(`Unsupported format for image copy: ${format}`)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Copy error:", error)
|
||||
toast.dismiss()
|
||||
@@ -445,9 +427,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
{authorName}
|
||||
</Link>
|
||||
)}
|
||||
{!authorData.html_url && (
|
||||
<span className="text-sm">{authorName}</span>
|
||||
)}
|
||||
{!authorData.html_url && <span className="text-sm">{authorName}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -696,6 +676,7 @@ export function IconDetails({ icon, iconData, authorData, allIcons }: IconDetail
|
||||
<Card className="bg-background/50 border shadow-lg">
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{/** biome-ignore lint/correctness/useUniqueElementIds: I want the ID to be fixed */}
|
||||
<h2 id="related-icons-title">Related Icons</h2>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Icon } from "@/types/icons"
|
||||
|
||||
import { useWindowVirtualizer } from "@tanstack/react-virtual"
|
||||
import { useEffect, useMemo, useRef, useState } from "react"
|
||||
import type { Icon } from "@/types/icons"
|
||||
import { IconCard } from "./icon-card"
|
||||
|
||||
interface IconsGridProps {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
"use client"
|
||||
|
||||
import { ArrowDownAZ, ArrowUpZA, Calendar, Filter, Search, SortAsc, X } from "lucide-react"
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||
import { useTheme } from "next-themes"
|
||||
import posthog from "posthog-js"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
import { VirtualizedIconsGrid } from "@/components/icon-grid"
|
||||
import { IconSubmissionContent } from "@/components/icon-submission-form"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
@@ -17,14 +23,8 @@ 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 { filterAndSortIcons, type SortOption } 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"
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||
import posthog from "posthog-js"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
export function IconSearch({ icons }: IconSearchProps) {
|
||||
const searchParams = useSearchParams()
|
||||
@@ -359,6 +359,7 @@ export function IconSearch({ icons }: IconSearchProps) {
|
||||
<p className="text-lg text-muted-foreground mt-2">Help us expand our collection</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 items-center w-full">
|
||||
{/** biome-ignore lint/correctness/useUniqueElementIds: I want the ID to be fixed */}
|
||||
<div id="icon-submission-content" className="w-full">
|
||||
<IconSubmissionContent />
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
import { DialogDescription } from "@radix-ui/react-dialog"
|
||||
import { ExternalLink, PlusCircle } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
|
||||
export const ISSUE_TEMPLATES = [
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
import { AnimatePresence, motion } from "framer-motion"
|
||||
import { X } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { REPO_PATH } from "@/constants"
|
||||
|
||||
const LOCAL_STORAGE_KEY = "licenseNoticeDismissed"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import { ArrowRight } from "lucide-react"
|
||||
import React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "../ui/button"
|
||||
|
||||
interface InteractiveHoverButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import type { ComponentPropsWithoutRef } from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { Marquee } from "@/components/magicui/marquee"
|
||||
import { BASE_URL } from "@/constants"
|
||||
import { cn, formatIconName } from "@/lib/utils"
|
||||
import type { Icon, IconWithName } from "@/types/icons"
|
||||
import { format, isToday, isYesterday } from "date-fns"
|
||||
import { ArrowRight, Clock, ExternalLink } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { Marquee } from "@/components/magicui/marquee"
|
||||
import { BASE_URL } from "@/constants"
|
||||
import { cn, formatIconName } from "@/lib/utils"
|
||||
import type { Icon, IconWithName } from "@/types/icons"
|
||||
|
||||
function formatIconDate(timestamp: string): string {
|
||||
const date = new Date(timestamp)
|
||||
@@ -71,13 +71,7 @@ export function RecentlyAddedIcons({ icons }: { icons: IconWithName[] }) {
|
||||
}
|
||||
|
||||
// Marquee-compatible icon card
|
||||
function RecentIconCard({
|
||||
name,
|
||||
data,
|
||||
}: {
|
||||
name: string
|
||||
data: Icon
|
||||
}) {
|
||||
function RecentIconCard({ name, data }: { name: string; data: Icon }) {
|
||||
const formattedIconName = formatIconName(name)
|
||||
return (
|
||||
<Link
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
import { Moon, Sun } from "lucide-react"
|
||||
import { useTheme } from "next-themes"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||
import { useState } from "react"
|
||||
|
||||
export function ThemeSwitcher() {
|
||||
const { setTheme } = useTheme()
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user