new Icon component with 3 icon variations

This commit is contained in:
Oliver Bryan
2026-01-17 04:44:55 +00:00
parent 8dc6291eab
commit c15a69a5ea
4 changed files with 229 additions and 8 deletions

View File

@@ -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 (
<IconComponent
size={size}
fill={
(iconStyle === "pixel" && icon === "moon") ||
(iconStyle === "pixel" && icon === "hash") ||
iconStyle === "phosphor"
? "var(--foreground)"
: "transparent"
}
{...props}
/>
);
}

View File

@@ -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 (
<main className="w-full h-[100vh] flex flex-col items-center justify-center gap-4 p-4">
<h1 className="text-3xl font-bold">Test</h1>
<main className="w-full min-h-[100vh] flex justify-center items-center gap-32 p-4">
<div className="mt-8 flex flex-col items-center gap-4">
<p className="text-muted-foreground">Other test components</p>
<div className="flex gap-4">
<Button linkTo="/">go back to "/"</Button>
</div>
<LogOutButton />
<ColourPicker colour={colour} onChange={setColour} />
<p className="text-muted-foreground">Simple test page for demo</p>
<div className="flex gap-4">
<Button linkTo="/">go back to "/"</Button>
<ThemeToggle />
</div>
<LogOutButton />
<ColourPicker colour={colour} onChange={setColour} />
<div>
<h1 className="text-2xl font-bold">Icon Demo</h1>
<p className="text-muted-foreground">
All {iconNames.length} icons across {iconStyles.length} styles
</p>
<div className="overflow-x-auto">
<table className="border-collapse">
<thead>
<tr className="border-b">
<th className="text-left p-2 font-medium">Name</th>
{iconStyles.map((iconStyle) => (
<th key={iconStyle} className="text-center p-2 font-medium capitalize">
{iconStyle}
</th>
))}
</tr>
</thead>
<tbody>
{iconNames.map((name) => (
<tr key={name} className="border-b hover:bg-muted/50">
<td className="font-mono text-sm pl-2 pr-12">{name}</td>
{iconStyles.map((iconStyle) => (
<td key={iconStyle} className="p-2 text-center">
<Icon icon={name} iconStyle={iconStyle} size={24} />
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</main>
);
}