From c15a69a5ea2b1ffc5d85ac9925e6d18a55b5c4a8 Mon Sep 17 00:00:00 2001 From: Oliver Bryan <04oliverbryan@gmail.com> Date: Sat, 17 Jan 2026 04:44:55 +0000 Subject: [PATCH] new Icon component with 3 icon variations --- bun.lock | 6 + packages/frontend/package.json | 4 +- packages/frontend/src/components/ui/icon.tsx | 176 +++++++++++++++++++ packages/frontend/src/pages/Test.tsx | 51 +++++- 4 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 packages/frontend/src/components/ui/icon.tsx diff --git a/bun.lock b/bun.lock index b38e528..4364b90 100644 --- a/bun.lock +++ b/bun.lock @@ -39,6 +39,8 @@ "version": "0.1.0", "dependencies": { "@iconify/react": "^6.0.2", + "@nsmr/pixelart-react": "^2.0.0", + "@phosphor-icons/react": "^2.1.10", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.8", @@ -262,6 +264,10 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@nsmr/pixelart-react": ["@nsmr/pixelart-react@2.0.0", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-hD9CeUZp10jemfnBQdN+mAMiIGnzcNDG8DoGcHUAvO6JgN1k7dbWscmY5HBqmWglnHliwcq4cY9ICQzjsaWeqQ=="], + + "@phosphor-icons/react": ["@phosphor-icons/react@2.1.10", "", { "peerDependencies": { "react": ">= 16.8", "react-dom": ">= 16.8" } }, "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], diff --git a/packages/frontend/package.json b/packages/frontend/package.json index fe87aad..075c68e 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -10,7 +10,8 @@ }, "dependencies": { "@iconify/react": "^6.0.2", - "@sprint/shared": "workspace:*", + "@nsmr/pixelart-react": "^2.0.0", + "@phosphor-icons/react": "^2.1.10", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.8", @@ -18,6 +19,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", + "@sprint/shared": "workspace:*", "@tailwindcss/vite": "^4.1.18", "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", diff --git a/packages/frontend/src/components/ui/icon.tsx b/packages/frontend/src/components/ui/icon.tsx new file mode 100644 index 0000000..290d460 --- /dev/null +++ b/packages/frontend/src/components/ui/icon.tsx @@ -0,0 +1,176 @@ +import { + Alert as PixelAlert, + Check as PixelCheck, + ChevronDown as PixelChevronDown, + ChevronLeft as PixelChevronLeft, + ChevronRight as PixelChevronRight, + ChevronUp as PixelChevronUp, + Circle as PixelCircle, + Clock as PixelClock, + Close as PixelClose, + Edit as PixelEdit, + Home as PixelHome, + InfoBox as PixelInfo, + Link as PixelLink, + Loader as PixelLoader, + Logout as PixelLogout, + Moon as PixelMoon, + MoreVertical as PixelMoreVertical, + NoteDelete as PixelNoteDelete, + Plus as PixelPlus, + Server as PixelServer, + Sun as PixelSun, + Trash as PixelTrash, + Undo as PixelUndo, + User as PixelUser, + ViewportWide as PixelViewportWide, +} from "@nsmr/pixelart-react"; +import { + CheckIcon as PhosphorCheck, + CheckCircleIcon as PhosphorCheckCircle, + CaretDownIcon as PhosphorChevronDown, + CaretLeftIcon as PhosphorChevronLeft, + CaretRightIcon as PhosphorChevronRight, + CaretUpIcon as PhosphorChevronUp, + CircleIcon as PhosphorCircle, + ClockIcon as PhosphorClock, + DotsSixVerticalIcon as PhosphorDotsSixVertical, + DotsThreeVerticalIcon as PhosphorDotsThreeVertical, + PencilSimpleIcon as PhosphorEdit, + HashIcon as PhosphorHash, + HashStraightIcon as PhosphorHashStraight, + HouseIcon as PhosphorHome, + InfoIcon as PhosphorInfo, + LinkIcon as PhosphorLink, + SpinnerGapIcon as PhosphorLoader, + SignOutIcon as PhosphorLogOut, + MoonIcon as PhosphorMoon, + OctagonIcon as PhosphorOctagon, + PlusIcon as PhosphorPlus, + QuestionIcon as PhosphorQuestion, + HardDrivesIcon as PhosphorServer, + SunIcon as PhosphorSun, + TrashIcon as PhosphorTrash, + ArrowCounterClockwiseIcon as PhosphorUndo, + UserIcon as PhosphorUser, + WarningIcon as PhosphorWarning, + XIcon as PhosphorX, +} from "@phosphor-icons/react"; +import { + AlertTriangle, + Check, + CheckIcon, + ChevronDown, + ChevronDownIcon, + ChevronLeftIcon, + ChevronRightIcon, + ChevronUp, + ChevronUpIcon, + CircleCheckIcon, + CircleIcon, + CircleQuestionMark, + Edit, + EllipsisVertical, + GripVerticalIcon, + Hash, + InfoIcon, + Link, + Loader, + Loader2Icon, + LogOut, + Home as LucideHome, + Moon, + OctagonXIcon, + Plus, + ServerIcon, + Sun, + Timer, + Trash, + TriangleAlertIcon, + Undo, + Undo2, + UserRound, + X, +} from "lucide-react"; + +const icons = { + alertTriangle: { lucide: AlertTriangle, pixel: PixelAlert, phosphor: PhosphorWarning }, + check: { lucide: Check, pixel: PixelCheck, phosphor: PhosphorCheck }, + checkIcon: { lucide: CheckIcon, pixel: PixelCheck, phosphor: PhosphorCheck }, + chevronDown: { lucide: ChevronDown, pixel: PixelChevronDown, phosphor: PhosphorChevronDown }, + chevronDownIcon: { lucide: ChevronDownIcon, pixel: PixelChevronDown, phosphor: PhosphorChevronDown }, + chevronLeftIcon: { lucide: ChevronLeftIcon, pixel: PixelChevronLeft, phosphor: PhosphorChevronLeft }, + chevronRightIcon: { lucide: ChevronRightIcon, pixel: PixelChevronRight, phosphor: PhosphorChevronRight }, + chevronUp: { lucide: ChevronUp, pixel: PixelChevronUp, phosphor: PhosphorChevronUp }, + chevronUpIcon: { lucide: ChevronUpIcon, pixel: PixelChevronUp, phosphor: PhosphorChevronUp }, + circleCheckIcon: { lucide: CircleCheckIcon, pixel: PixelCheck, phosphor: PhosphorCheckCircle }, + circleIcon: { lucide: CircleIcon, pixel: PixelCircle, phosphor: PhosphorCircle }, + circleQuestionMark: { lucide: CircleQuestionMark, pixel: PixelNoteDelete, phosphor: PhosphorQuestion }, + edit: { lucide: Edit, pixel: PixelEdit, phosphor: PhosphorEdit }, + ellipsisVertical: { + lucide: EllipsisVertical, + pixel: PixelMoreVertical, + phosphor: PhosphorDotsThreeVertical, + }, + gripVerticalIcon: { + lucide: GripVerticalIcon, + pixel: PixelViewportWide, + phosphor: PhosphorDotsSixVertical, + }, + hash: { lucide: Hash, pixel: PhosphorHashStraight, phosphor: PhosphorHash }, + home: { lucide: LucideHome, pixel: PixelHome, phosphor: PhosphorHome }, + infoIcon: { lucide: InfoIcon, pixel: PixelInfo, phosphor: PhosphorInfo }, + link: { lucide: Link, pixel: PixelLink, phosphor: PhosphorLink }, + loader: { lucide: Loader, pixel: PixelLoader, phosphor: PhosphorLoader }, + loader2Icon: { lucide: Loader2Icon, pixel: PixelLoader, phosphor: PhosphorLoader }, + logOut: { lucide: LogOut, pixel: PixelLogout, phosphor: PhosphorLogOut }, + moon: { lucide: Moon, pixel: PixelMoon, phosphor: PhosphorMoon }, + octagonXIcon: { lucide: OctagonXIcon, pixel: PixelClose, phosphor: PhosphorOctagon }, + plus: { lucide: Plus, pixel: PixelPlus, phosphor: PhosphorPlus }, + serverIcon: { lucide: ServerIcon, pixel: PixelServer, phosphor: PhosphorServer }, + sun: { lucide: Sun, pixel: PixelSun, phosphor: PhosphorSun }, + timer: { lucide: Timer, pixel: PixelClock, phosphor: PhosphorClock }, + trash: { lucide: Trash, pixel: PixelTrash, phosphor: PhosphorTrash }, + triangleAlertIcon: { lucide: TriangleAlertIcon, pixel: PixelAlert, phosphor: PhosphorWarning }, + undo: { lucide: Undo, pixel: PixelUndo, phosphor: PhosphorUndo }, + undo2: { lucide: Undo2, pixel: PixelUndo, phosphor: PhosphorUndo }, + userRound: { lucide: UserRound, pixel: PixelUser, phosphor: PhosphorUser }, + x: { lucide: X, pixel: PixelClose, phosphor: PhosphorX }, +}; + +export type IconName = keyof typeof icons; +export const iconNames = Object.keys(icons) as IconName[]; +export const iconStyles = ["lucide", "pixel", "phosphor"] as const; +export type IconStyle = (typeof iconStyles)[number]; + +export default function Icon({ + icon, + iconStyle = "lucide", + size = 24, + ...props +}: { + icon: IconName; + iconStyle?: IconStyle; + size?: number | string; + color?: string; +} & React.ComponentProps<"svg">) { + const IconComponent = icons[icon]?.[iconStyle]; + + if (!IconComponent) { + return null; + } + + return ( + + ); +} diff --git a/packages/frontend/src/pages/Test.tsx b/packages/frontend/src/pages/Test.tsx index 0e195dc..08572c8 100644 --- a/packages/frontend/src/pages/Test.tsx +++ b/packages/frontend/src/pages/Test.tsx @@ -1,22 +1,59 @@ import { useState } from "react"; import LogOutButton from "@/components/log-out-button"; +import ThemeToggle from "@/components/theme-toggle"; import { Button } from "@/components/ui/button"; import ColourPicker from "@/components/ui/colour-picker"; +import Icon, { iconNames, iconStyles } from "@/components/ui/icon"; function Test() { const [colour, setColour] = useState("#e05656"); return ( -
-

Test

+
+
+

Other test components

+
+ +
+ + -

Simple test page for demo

-
- +
- - +
+

Icon Demo

+

+ All {iconNames.length} icons across {iconStyles.length} styles +

+ +
+ + + + + {iconStyles.map((iconStyle) => ( + + ))} + + + + {iconNames.map((name) => ( + + + {iconStyles.map((iconStyle) => ( + + ))} + + ))} + +
Name + {iconStyle} +
{name} + +
+
+
); }