11 changed files with 213 additions and 110 deletions
@ -0,0 +1,99 @@ |
|||||
|
import { cn } from "@app/core/utils/cn"; |
||||
|
import type React from "react"; |
||||
|
|
||||
|
type RGBColor = { |
||||
|
r: number; |
||||
|
g: number; |
||||
|
b: number; |
||||
|
a: number; |
||||
|
}; |
||||
|
|
||||
|
interface AvatarProps { |
||||
|
text: string; |
||||
|
size?: "sm" | "md" | "lg" | "xl"; |
||||
|
className?: string; |
||||
|
} |
||||
|
|
||||
|
// biome-ignore lint/complexity/noStaticOnlyClass: stop being annoying Biome
|
||||
|
class ColorUtils { |
||||
|
static hexToRgb(hex: number): RGBColor { |
||||
|
return { |
||||
|
r: (hex & 0xff0000) >> 16, |
||||
|
g: (hex & 0x00ff00) >> 8, |
||||
|
b: hex & 0x0000ff, |
||||
|
a: 255, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
static rgbToHex(color: RGBColor): number { |
||||
|
return ( |
||||
|
(Math.round(color.a) << 24) | |
||||
|
(Math.round(color.r) << 16) | |
||||
|
(Math.round(color.g) << 8) | |
||||
|
Math.round(color.b) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
static isLight(color: RGBColor): boolean { |
||||
|
const brightness = (color.r * 299 + color.g * 587 + color.b * 114) / 1000; |
||||
|
return brightness > 127.5; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const Avatar: React.FC<AvatarProps> = ({ |
||||
|
text, |
||||
|
size = "md", |
||||
|
className, |
||||
|
}) => { |
||||
|
console.log(text); |
||||
|
|
||||
|
const sizes = { |
||||
|
sm: "size-12 text-sm", |
||||
|
md: "size-12 text-sm", |
||||
|
lg: "size-16 text-lg", |
||||
|
xl: "size-29 text-xl", |
||||
|
}; |
||||
|
|
||||
|
// Generate color from text
|
||||
|
const getColorFromText = (): RGBColor => { |
||||
|
let hash = 0; |
||||
|
for (let i = 0; i < text.length; i++) { |
||||
|
hash = text.charCodeAt(i) + ((hash << 5) - hash); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
r: (hash & 0xff0000) >> 16, |
||||
|
g: (hash & 0x00ff00) >> 8, |
||||
|
b: hash & 0x0000ff, |
||||
|
a: 255, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
const bgColor = getColorFromText(); |
||||
|
const isLight = ColorUtils.isLight(bgColor); |
||||
|
const textColor = isLight ? "#000000" : "#FFFFFF"; |
||||
|
const initials = text |
||||
|
.toUpperCase() |
||||
|
.slice(0, 4); |
||||
|
|
||||
|
return ( |
||||
|
<div |
||||
|
className={cn(` |
||||
|
rounded-full |
||||
|
flex |
||||
|
items-center |
||||
|
justify-center |
||||
|
font-semibold`,
|
||||
|
sizes[size], |
||||
|
className) |
||||
|
} |
||||
|
style={{ |
||||
|
backgroundColor: `rgb(${bgColor.r}, ${bgColor.g}, ${bgColor.b})`, |
||||
|
color: textColor, |
||||
|
}} |
||||
|
> |
||||
|
<p className="p-1">{initials}</p> |
||||
|
|
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
@ -1,17 +1,21 @@ |
|||||
|
import { cn } from "@app/core/utils/cn"; |
||||
import { H4 } from "@components/UI/Typography/H4.tsx"; |
import { H4 } from "@components/UI/Typography/H4.tsx"; |
||||
|
import type { JSX } from "react"; |
||||
|
|
||||
export interface SidebarSectionProps { |
export interface SidebarSectionProps { |
||||
label: string; |
label: string; |
||||
subheader?: string; |
subheader?: string; |
||||
children: React.ReactNode; |
children: React.ReactNode; |
||||
|
className?: string; |
||||
} |
} |
||||
|
|
||||
export const SidebarSection = ({ |
export const SidebarSection = ({ |
||||
label: title, |
label: title, |
||||
children, |
children, |
||||
|
className, |
||||
}: SidebarSectionProps): JSX.Element => ( |
}: SidebarSectionProps): JSX.Element => ( |
||||
<div className="px-4 py-2"> |
<div className="px-4 py-2"> |
||||
<H4 className="mb-2 ml-2">{title}</H4> |
<H4 className="mb-3 ml-2">{title}</H4> |
||||
<div className="space-y-1">{children}</div> |
<div className={cn("space-y-1", className)}>{children}</div> |
||||
</div> |
</div> |
||||
); |
); |
||||
|
|||||
Loading…
Reference in new issue