All checks were successful
ci/woodpecker/push/web Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import type { CSSProperties } from "react";
|
|
|
|
export interface MosaicLogoProps {
|
|
/** Width and height in pixels (default: 36) */
|
|
size?: number;
|
|
/** Whether to animate rotation (default: false) */
|
|
spinning?: boolean;
|
|
/** Seconds for one full rotation (default: 20) */
|
|
spinDuration?: number;
|
|
/** Additional CSS classes for the root element */
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* MosaicLogo renders the 5-element Mosaic logo icon:
|
|
* - 4 corner squares (blue, purple, teal, amber)
|
|
* - 1 center circle (pink)
|
|
*
|
|
* Colors use CSS custom properties so they respond to theme changes.
|
|
* When `spinning` is true the logo rotates continuously, making it
|
|
* suitable for use as a loading indicator.
|
|
*/
|
|
export function MosaicLogo({
|
|
size = 36,
|
|
spinning = false,
|
|
spinDuration = 20,
|
|
className = "",
|
|
}: MosaicLogoProps): React.JSX.Element {
|
|
// Scale factor relative to the 36px reference design
|
|
const scale = size / 36;
|
|
|
|
// Derived dimensions
|
|
const squareSize = Math.round(14 * scale);
|
|
const circleSize = Math.round(11 * scale);
|
|
const borderRadius = Math.round(3 * scale);
|
|
|
|
const animationValue = spinning
|
|
? `mosaicLogoSpin ${String(spinDuration)}s linear infinite`
|
|
: undefined;
|
|
|
|
const containerStyle: CSSProperties = {
|
|
width: size,
|
|
height: size,
|
|
position: "relative",
|
|
flexShrink: 0,
|
|
animation: animationValue,
|
|
transformOrigin: "center",
|
|
};
|
|
|
|
const baseSquareStyle: CSSProperties = {
|
|
position: "absolute",
|
|
width: squareSize,
|
|
height: squareSize,
|
|
borderRadius,
|
|
};
|
|
|
|
const circleStyle: CSSProperties = {
|
|
position: "absolute",
|
|
top: "50%",
|
|
left: "50%",
|
|
transform: "translate(-50%, -50%)",
|
|
width: circleSize,
|
|
height: circleSize,
|
|
borderRadius: "50%",
|
|
background: "var(--ms-pink-500)",
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{spinning && (
|
|
<style>{`
|
|
@keyframes mosaicLogoSpin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`}</style>
|
|
)}
|
|
<div style={containerStyle} className={className} role="img" aria-label="Mosaic logo">
|
|
{/* Top-left: blue */}
|
|
<div style={{ ...baseSquareStyle, top: 0, left: 0, background: "var(--ms-blue-500)" }} />
|
|
{/* Top-right: purple */}
|
|
<div style={{ ...baseSquareStyle, top: 0, right: 0, background: "var(--ms-purple-500)" }} />
|
|
{/* Bottom-right: teal */}
|
|
<div
|
|
style={{ ...baseSquareStyle, bottom: 0, right: 0, background: "var(--ms-teal-500)" }}
|
|
/>
|
|
{/* Bottom-left: amber */}
|
|
<div
|
|
style={{ ...baseSquareStyle, bottom: 0, left: 0, background: "var(--ms-amber-500)" }}
|
|
/>
|
|
{/* Center: pink circle */}
|
|
<div style={circleStyle} />
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default MosaicLogo;
|