120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import { useId } from "react";
|
||
|
||
export type DockSymbol = "home" | "about" | "portfolio" | "products" | "contact";
|
||
|
||
/**
|
||
* macOS Big Sur–style squircle app icon, drawn entirely in SVG so it stays
|
||
* crisp at every magnification step. Inactive icons use a neutral graphite
|
||
* gradient; the active icon lights up in the brand coral.
|
||
*/
|
||
export function DockAppIcon({
|
||
symbol,
|
||
active = false,
|
||
disabled = false,
|
||
}: {
|
||
symbol: DockSymbol;
|
||
active?: boolean;
|
||
disabled?: boolean;
|
||
}) {
|
||
const uid = useId().replace(/[:]/g, "");
|
||
const bg = `bg-${uid}`;
|
||
const gloss = `gloss-${uid}`;
|
||
|
||
return (
|
||
<svg
|
||
viewBox="0 0 100 100"
|
||
className="h-full w-full"
|
||
role="presentation"
|
||
style={disabled ? { opacity: 0.55 } : undefined}
|
||
>
|
||
<defs>
|
||
<linearGradient id={bg} x1="0" y1="0" x2="0" y2="1">
|
||
{active ? (
|
||
<>
|
||
<stop offset="0" stopColor="#ff8a5f" />
|
||
<stop offset="1" stopColor="#e5431f" />
|
||
</>
|
||
) : (
|
||
<>
|
||
<stop offset="0" stopColor="#454b57" />
|
||
<stop offset="1" stopColor="#1c2027" />
|
||
</>
|
||
)}
|
||
</linearGradient>
|
||
<linearGradient id={gloss} x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0" stopColor="#ffffff" stopOpacity="0.28" />
|
||
<stop offset="0.5" stopColor="#ffffff" stopOpacity="0" />
|
||
</linearGradient>
|
||
</defs>
|
||
|
||
{/* tile */}
|
||
<rect x="4" y="4" width="92" height="92" rx="26" fill={`url(#${bg})`} />
|
||
<rect x="4" y="4" width="92" height="92" rx="26" fill={`url(#${gloss})`} />
|
||
<rect
|
||
x="4.75"
|
||
y="4.75"
|
||
width="90.5"
|
||
height="90.5"
|
||
rx="25.25"
|
||
fill="none"
|
||
stroke="#ffffff"
|
||
strokeOpacity="0.14"
|
||
strokeWidth="1.5"
|
||
/>
|
||
|
||
{/* symbol */}
|
||
<g
|
||
fill="none"
|
||
stroke="#ffffff"
|
||
strokeWidth="6"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
>
|
||
{symbol === "home" && (
|
||
<>
|
||
<path d="M29 49 L50 30 L71 49" />
|
||
<path d="M34 46 V70 H66 V46" />
|
||
</>
|
||
)}
|
||
{symbol === "about" && (
|
||
<>
|
||
<circle cx="50" cy="41" r="11" />
|
||
<path d="M30 72 C30 57, 70 57, 70 72" />
|
||
</>
|
||
)}
|
||
{symbol === "contact" && (
|
||
<>
|
||
<rect x="27" y="35" width="46" height="30" rx="6" />
|
||
<path d="M30 40 L50 54 L70 40" />
|
||
</>
|
||
)}
|
||
</g>
|
||
|
||
{/* filled symbols */}
|
||
{symbol === "portfolio" && (
|
||
<g fill="#ffffff">
|
||
<rect x="32" y="32" width="15" height="15" rx="4" />
|
||
<rect x="53" y="32" width="15" height="15" rx="4" />
|
||
<rect x="32" y="53" width="15" height="15" rx="4" />
|
||
<rect x="53" y="53" width="15" height="15" rx="4" />
|
||
</g>
|
||
)}
|
||
{symbol === "products" && (
|
||
<g
|
||
fill="none"
|
||
stroke="#ffffff"
|
||
strokeWidth="6"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
>
|
||
<path d="M50 28 L71 40 L71 60 L50 72 L29 60 L29 40 Z" />
|
||
<path d="M29 40 L50 52 L71 40" />
|
||
<path d="M50 52 L50 72" />
|
||
</g>
|
||
)}
|
||
</svg>
|
||
);
|
||
}
|