Files
ersatztv/design-system/components/data-display/ChannelLogo.jsx
T
2026-07-02 07:37:00 +02:00

52 lines
1.5 KiB
React

import React from "react";
const palette = [
["#5B7CFA", "#2E3A66"], ["#3FB984", "#1E4536"], ["#E0A83D", "#4A3818"],
["#E5484D", "#4A1F21"], ["#B06CF0", "#38235A"], ["#48B0C8", "#193E47"],
];
function hashIndex(str) {
let h = 0;
for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) >>> 0;
return h % palette.length;
}
/**
* Channel logo tile. Renders an image when given, else a generated
* initials chip keyed to the channel name — mirrors ErsatzTV's logo/gen.
*/
export function ChannelLogo({ name = "", src = null, size = 40, radius = "var(--radius-sm)", style = {} }) {
const [fg, bg] = palette[hashIndex(name)];
const initials = name
.split(/[\s\-|:]+/)
.filter(Boolean)
.slice(0, 2)
.map((w) => w[0])
.join("")
.toUpperCase() || "?";
return (
<span
style={{
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: size,
height: size,
flex: "0 0 auto",
overflow: "hidden",
borderRadius: radius,
background: src ? "var(--ctv-bg-sunken)" : bg,
border: "1px solid var(--border-hairline)",
...style,
}}
>
{src ? (
<img src={src} alt={name} style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} />
) : (
<span style={{ font: `var(--weight-semibold) ${Math.round(size * 0.34)}px/1 var(--font-mono)`, color: fg, letterSpacing: "0.02em" }}>{initials}</span>
)}
</span>
);
}