feat: [WIP] add radar component

This commit is contained in:
Mathias Schopmans
2024-02-20 15:39:43 +01:00
committed by Mathias Schopmans
parent 563d8debc0
commit 1b7634a2ef
5 changed files with 138 additions and 5 deletions

View File

@@ -34,25 +34,33 @@
"id": "adopt", "id": "adopt",
"title": "Adopt", "title": "Adopt",
"description": "We can clearly recommend this technology. We have used it for longer period of time in many teams and it has proven to be stable and useful.", "description": "We can clearly recommend this technology. We have used it for longer period of time in many teams and it has proven to be stable and useful.",
"color": "#5cb449" "color": "#5cb449",
"radius": 0.5,
"strokeWidth": 6
}, },
{ {
"id": "trial", "id": "trial",
"title": "Trial", "title": "Trial",
"description": "We have used it with success and recommend to have a closer look at the technology in this ring. The goal of items here is to look at them more closely, with the goal to bring them to the adopt level.", "description": "We have used it with success and recommend to have a closer look at the technology in this ring. The goal of items here is to look at them more closely, with the goal to bring them to the adopt level.",
"color": "#faa03d" "color": "#faa03d",
"radius": 0.7,
"strokeWidth": 4
}, },
{ {
"id": "assess", "id": "assess",
"title": "Assess", "title": "Assess",
"description": "We have tried it out and we find it promising. We recommend having a look at these items when you face a specific need for the technology in your project.", "description": "We have tried it out and we find it promising. We recommend having a look at these items when you face a specific need for the technology in your project.",
"color": "#029df7" "color": "#029df7",
"radius": 0.88,
"strokeWidth": 2
}, },
{ {
"id": "hold", "id": "hold",
"title": "Hold", "title": "Hold",
"description": "This category is a bit special. Unlike the others, we recommend to stop doing or using something. That does not mean that they are bad and it often might be ok to use them in existing projects. But we move things here if we think we shouldn't do them anymore - because we see better options or alternatives now.", "description": "This category is a bit special. Unlike the others, we recommend to stop doing or using something. That does not mean that they are bad and it often might be ok to use them in existing projects. But we move things here if we think we shouldn't do them anymore - because we see better options or alternatives now.",
"color": "#688190" "color": "#688190",
"radius": 1,
"strokeWidth": 1
} }
], ],
"flags": { "flags": {

View File

@@ -0,0 +1,11 @@
.radar {
padding: 50px;
position: relative;
}
.svg {
display: block;
max-width: 100%;
height: auto;
margin: 0 auto;
}

View File

@@ -0,0 +1,102 @@
import React, { FC } from "react";
import styles from "./Radar.module.css";
import { Quadrant, Ring } from "@/lib/types";
export interface RadarProps {
size?: number;
quadrants: Quadrant[];
rings: Ring[];
}
export const Radar: FC<RadarProps> = ({ size = 800, quadrants, rings }) => {
const viewBoxSize = size;
const center = size / 2;
const startAngles = [270, 0, 180, 90]; // Corresponding to positions 1, 2, 3, and 4 respectively
// Helper function to convert polar coordinates to cartesian
const polarToCartesian = (
radius: number,
angleInDegrees: number,
): { x: number; y: number } => {
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
return {
x: center + radius * Math.cos(angleInRadians),
y: center + radius * Math.sin(angleInRadians),
};
};
// Function to generate the path for a ring segment
const describeArc = (radiusPercentage: number, position: number): string => {
// Define the start and end angles based on the quadrant position
const startAngle = startAngles[position - 1];
const endAngle = startAngle + 90;
const radius = radiusPercentage * center; // Convert percentage to actual radius
const start = polarToCartesian(radius, endAngle);
const end = polarToCartesian(radius, startAngle);
// prettier-ignore
return [
"M", start.x, start.y,
"A", radius, radius, 0, 0, 0, end.x, end.y,
].join(" ");
};
const renderGlow = (position: number, color: string) => {
const gradientId = `glow-${position}`;
const cx = position === 1 || position === 3 ? 1 : 0;
const cy = position === 1 || position === 2 ? 1 : 0;
const x = position === 1 || position === 3 ? 0 : center;
const y = position === 1 || position === 2 ? 0 : center;
return (
<>
<defs>
<radialGradient id={gradientId} x={0} y={0} r={1} cx={cx} cy={cy}>
<stop offset="0%" stopColor={color} stopOpacity={0.5}></stop>
<stop offset="100%" stopColor={color} stopOpacity={0}></stop>
</radialGradient>
</defs>
<rect
width={center}
height={center}
x={x}
y={y}
fill={`url(#${gradientId})`}
/>
</>
);
};
return (
<div className={styles.radar}>
<svg
className={styles.svg}
width={viewBoxSize}
height={viewBoxSize}
viewBox={`0 0 ${viewBoxSize} ${viewBoxSize}`}
>
{quadrants.map((quadrant) => (
<g className={`quadrant quadrant-${quadrant.id}`} key={quadrant.id}>
{renderGlow(quadrant.position, quadrant.color)}
{rings.map((ring) => (
<path
key={`${ring.id}-${quadrant.id}`}
data-key={`${ring.id}-${quadrant.id}`}
d={describeArc(ring.radius || 0.5, quadrant.position)}
fill="none"
stroke={quadrant.color}
strokeWidth={ring.strokeWidth || 2}
/>
))}
</g>
))}
</svg>
</div>
);
};
export default Radar;

View File

@@ -30,6 +30,8 @@ export interface Ring {
title: string; title: string;
description: string; description: string;
color: string; color: string;
radius?: number;
strokeWidth?: number;
} }
export interface Quadrant { export interface Quadrant {

View File

@@ -1,10 +1,19 @@
import { QuadrantList } from "@/components/QuadrantList/QuadrantList"; import { QuadrantList } from "@/components/QuadrantList/QuadrantList";
import { getAppName, getItems, getReleases } from "@/lib/data"; import { Radar } from "@/components/Radar/Radar";
import {
getAppName,
getItems,
getQuadrants,
getReleases,
getRings,
} from "@/lib/data";
import { CustomPage } from "@/pages/_app"; import { CustomPage } from "@/pages/_app";
const Home: CustomPage = () => { const Home: CustomPage = () => {
const appName = getAppName(); const appName = getAppName();
const version = getReleases().length; const version = getReleases().length;
const rings = getRings();
const quadrants = getQuadrants();
const items = getItems(undefined, true); const items = getItems(undefined, true);
return ( return (
<> <>
@@ -12,6 +21,7 @@ const Home: CustomPage = () => {
{appName}{" "} {appName}{" "}
<span style={{ color: "var(--highlight)" }}>Version #{version}</span> <span style={{ color: "var(--highlight)" }}>Version #{version}</span>
</h1> </h1>
<Radar quadrants={quadrants} rings={rings} />
<QuadrantList items={items} /> <QuadrantList items={items} />
</> </>
); );