| 1 | import type { ProjectIconColor } from "@t3tools/contracts"; |
| 2 | import { projectIconColorClassName } from "../projectIconColors"; |
| 3 | import { cn } from "~/lib/utils"; |
| 4 | |
| 5 | const monogramSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" }); |
| 6 | |
| 7 | export function ProjectMonogram({ |
| 8 | text, |
| 9 | color, |
| 10 | className, |
| 11 | }: { |
| 12 | readonly text: string; |
| 13 | readonly color: ProjectIconColor; |
| 14 | readonly className?: string | undefined; |
| 15 | }) { |
| 16 | // Wrapped like the emoji and Lucide branches so the monogram sits where an |
| 17 | // <img> favicon would. Menu items, buttons and the like pull every bare svg |
| 18 | // in with [&_svg]:-mx-0.5 to trim the padding stroke icons carry, and this |
| 19 | // tile has no such padding. |
| 20 | return ( |
| 21 | <span |
| 22 | aria-hidden="true" |
| 23 | className={cn("inline-flex size-4 shrink-0 items-center justify-center", className)} |
| 24 | > |
| 25 | <svg |
| 26 | viewBox="0 0 16 16" |
| 27 | className={cn( |
| 28 | "size-full overflow-hidden rounded-[25%] font-mono select-none", |
| 29 | projectIconColorClassName(color), |
| 30 | )} |
| 31 | style={{ |
| 32 | backgroundColor: "color-mix(in srgb, currentColor 14%, transparent)", |
| 33 | }} |
| 34 | > |
| 35 | <text |
| 36 | x="8" |
| 37 | y="10.8" |
| 38 | textAnchor="middle" |
| 39 | fill="currentColor" |
| 40 | className="font-mono" |
| 41 | fontSize="8.25" |
| 42 | fontWeight="700" |
| 43 | textLength={Array.from(monogramSegmenter.segment(text)).length === 1 ? 6 : 12} |
| 44 | lengthAdjust="spacingAndGlyphs" |
| 45 | textRendering="geometricPrecision" |
| 46 | > |
| 47 | {text} |
| 48 | </text> |
| 49 | </svg> |
| 50 | </span> |
| 51 | ); |
| 52 | } |