refactor: rename containers as docker compositions, as they are

This commit is contained in:
Michele Cereda
2024-09-14 23:10:17 +02:00
parent 31c313b3ac
commit df1d945284
91 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
/.techradar
/build
/node_modules
/package-lock.json

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env make
bootstrap:
@npm install
@${MAKE} staticsite-build
staticsite-build:
@npm run build
local-serve: staticsite-build
@npm run serve
composition-go-brrr: staticsite-build
@docker compose up -d
composition-stop:
@docker compose down
composition-refresh: composition-stop composition-go-brrr

View File

@@ -0,0 +1,21 @@
# Tech radar
Based upon [AOE's technology radar](https://github.com/AOEpeople/aoe_technology_radar).
Bootstrap:
1. Run `npm install`.
1. Run `npm run build` to create the initial radar.<br/>
This will also create a basic bootstrap of all required files, including `config.json` and `about.md` if they do not
exist yet.
Customization:
1. Change the `about.md`, `config.json`, `custom.css` files accordingly.
1. Create a folder named as a date in the `radar` folder to create a new revision.
1. Add markdown files in there for each change. Follow the instructions of the base.
Serve:
1. Execute `docker compose up -d`.
1. Open your browser at <http://localhost:8080/techradar/>.

View File

@@ -0,0 +1,11 @@
# How to use the Technology Radar
## Introduction
Edit this file to your needs to provide an introduction to the technology radar. Explain the purpose
of the radar and how it is created. This is a good place to explain the quadrants and rings, too.
## Contributing to the AOE Technology Radar
Contributions and source code of the AOE Tech Radar are on
GitHub: [AOE Tech Radar on GitHub](https://github.com/AOEpeople/aoe_technology_radar)

View File

@@ -0,0 +1,134 @@
{
"basePath": "/techradar",
"baseUrl": "",
"logoFile": "logo.svg",
"toggles": {
"showChart": true,
"showTagFilter": true,
"showQuadrantList": true,
"showEmptyRings": false
},
"sections": [
"radar",
"tags",
"list"
],
"colors": {
"foreground": "#fcf2e6",
"background": "#113521",
"highlight": "#d4a373",
"content": "#fff",
"text": "#575757",
"link": "#bc6c25",
"border": "rgba(255, 255, 255, 0.1)",
"tag": "rgba(255, 255, 255, 0.1)"
},
"quadrants": [
{
"id": "languages-and-frameworks",
"title": "Languages & Frameworks",
"description": "A selection of programming languages, alongside essential frameworks for building a variety of custom software.",
"color": "#a3b18a"
},
{
"id": "methods-and-patterns",
"title": "Methods & Patterns",
"description": "Key software development methods and design patterns, covering everything from continuous integration and testing to architecture.",
"color": "#588157"
},
{
"id": "platforms-and-operations",
"title": "Platforms & Operations",
"description": "Technologies and tools for software and infrastructure operations, including platforms and services for managing and scaling applications.",
"color": "#3f633e"
},
{
"id": "tools",
"title": "Tools",
"description": "A range of software tools, from simple productivity enhancers to comprehensive project solutions, catering to various project needs.",
"color": "#40713f"
}
],
"rings": [
{
"id": "adopt",
"title": "Adopt",
"description": "",
"color": "#588157",
"radius": 0.5,
"strokeWidth": 5
},
{
"id": "trial",
"title": "Trial",
"description": "",
"color": "#457b9d",
"radius": 0.69,
"strokeWidth": 3
},
{
"id": "assess",
"title": "Assess",
"description": "",
"color": "#bc6c25",
"radius": 0.85,
"strokeWidth": 2
},
{
"id": "hold",
"title": "Hold",
"description": "",
"color": "#d62828",
"radius": 1,
"strokeWidth": 0.75
}
],
"flags": {
"new": {
"color": "#f1235a",
"title": "New",
"titleShort": "N",
"description": "New in this version"
},
"changed": {
"color": "#40a7d1",
"title": "Changed",
"titleShort": "C",
"description": "Recently changed"
},
"default": {
"description": "Unchanged"
}
},
"chart": {
"size": 800,
"blipSize": 12
},
"social": [
{
"href": "https://github.com/mcereda",
"icon": "github"
},
{
"href": "https://gitlab.com/mckie",
"icon": "gitlab"
}
],
"imprint": "",
"labels": {
"title": "Technology Radar",
"imprint": "Legal Information",
"quadrant": "Quadrant",
"quadrantOverview": "Quadrant Overview",
"zoomIn": "Zoom in",
"filterByTag": "Filter by Tag",
"footer": "The technology radar is using a project by AOE GmbH. Feel free to build your own radar based on their open source project.",
"notUpdated": "This item was not updated in last three versions of the Radar. Should it have appeared in one of the more recent editions, there is a good chance it remains pertinent. However, if the item dates back further, its relevance may have diminished and our current evaluation could vary. Regrettably, my capacity to consistently revisit items from past Radar editions is limited.",
"notFound": "404 - Page not found",
"pageAbout": "About",
"pageOverview": "Technologies Overview",
"pageSearch": "Search",
"searchPlaceholder": "What are you looking for?",
"metaDescription": ""
}
}

View File

@@ -0,0 +1,6 @@
/* Use this file to optionally override global css styles and use with caution. */
/* See README.md for hints and examples: https://github.com/AOEpeople/aoe_technology_radar/ */
[class^="Layout_content"] span {
font-size: small;
}

View File

@@ -0,0 +1,13 @@
---
version: '3'
services:
radar:
container_name: radar
image: nginxinc/nginx-unprivileged:1.25.5-bookworm-perl
volumes:
- ${PWD}/build:/usr/share/nginx/html/techradar:ro
ports:
- 8080:8080
environment:
NGINX_HOST: localhost
NGINX_PORT: '8080'

View File

@@ -0,0 +1,12 @@
{
"name": "techradar",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"build": "techradar build",
"serve": "techradar serve"
},
"dependencies": {
"aoe_technology_radar": "^4"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 149.6 64.73"><path d="M25.87,0h-1.77v7.24C10.65,8.15,0,19.35,0,33.02s11.62,25.87,25.87,25.87,24.87-10.65,25.79-24.1h7.24v-1.77C58.89,14.81,44.09,0,25.87,0ZM25.87,55.32c-12.31,0-22.32-10.01-22.32-22.32S12.59,11.67,24.07,10.76v24.01h24.01c-.92,11.48-10.54,20.52-22.24,20.52h0l.03.03ZM51.74,31.22h-24.1V3.63c14.81.89,26.7,12.78,27.59,27.62h-3.52l.03-.03Z" fill="#fff" fill-rule="evenodd" stroke-width="0"/><path d="M78.09,10.41h-6.09v16.5h-1.5V10.41h-6.08v-1.27h13.67v1.27Z" fill="#fff" stroke-width="0"/><path d="M90.78,18.39h-8.31v7.25h9.56v1.27h-11.06V9.14h11v1.27h-9.5v6.71h8.31v1.27Z" fill="#fff" stroke-width="0"/><path d="M107.77,21.37c-.2,1.87-.87,3.3-2.01,4.3-1.13.99-2.65,1.49-4.53,1.49-1.32,0-2.48-.33-3.5-.99-1.01-.66-1.8-1.59-2.35-2.8-.55-1.21-.83-2.59-.84-4.14v-2.31c0-1.58.28-2.98.83-4.2.55-1.22,1.35-2.16,2.39-2.83,1.04-.66,2.23-1,3.58-1,1.9,0,3.41.51,4.51,1.54,1.1,1.03,1.74,2.45,1.92,4.26h-1.51c-.38-3.02-2.01-4.53-4.92-4.53-1.61,0-2.9.6-3.85,1.81s-1.43,2.87-1.43,5v2.17c0,2.05.46,3.69,1.4,4.91.93,1.22,2.19,1.83,3.78,1.83s2.75-.38,3.55-1.13c.8-.75,1.29-1.88,1.48-3.39h1.51Z" fill="#fff" stroke-width="0"/><path d="M124.59,26.91h-1.51v-8.52h-10.16v8.52h-1.5V9.14h1.5v7.98h10.16v-7.98h1.51v17.77Z" fill="#fff" stroke-width="0"/><path d="M71.73,41.62h-2.32v6.29h-4.28v-17.77h6.99c2.11,0,3.76.47,4.94,1.4,1.19.93,1.78,2.26,1.78,3.96,0,1.24-.25,2.26-.75,3.07-.5.81-1.28,1.47-2.35,1.98l3.71,7.18v.18h-4.59l-3.14-6.29ZM69.41,38.33h2.71c.81,0,1.43-.21,1.84-.64.41-.43.62-1.03.62-1.79s-.21-1.37-.62-1.81-1.03-.65-1.83-.65h-2.71v4.9Z" fill="#fff" stroke-width="0"/><path d="M91.53,44.59h-5.87l-1.03,3.32h-4.58l6.52-17.77h4.03l6.57,17.77h-4.6l-1.04-3.32ZM86.69,41.28h3.82l-1.92-6.16-1.9,6.16Z" fill="#fff" stroke-width="0"/><path d="M98.47,47.91v-17.77h5.73c1.57,0,2.98.36,4.24,1.07,1.25.71,2.23,1.72,2.94,3.01s1.06,2.75,1.07,4.36v.82c0,1.63-.34,3.09-1.03,4.38-.69,1.29-1.66,2.3-2.91,3.03-1.25.73-2.64,1.1-4.18,1.1h-5.85ZM102.75,33.44v11.17h1.49c1.23,0,2.17-.44,2.83-1.31.66-.87.99-2.17.99-3.9v-.77c0-1.72-.33-3.01-.99-3.88-.66-.87-1.62-1.31-2.88-1.31h-1.44Z" fill="#fff" stroke-width="0"/><path d="M124.47,44.59h-5.87l-1.03,3.32h-4.58l6.52-17.77h4.03l6.57,17.77h-4.6l-1.04-3.32ZM119.62,41.28h3.82l-1.92-6.16-1.9,6.16Z" fill="#fff" stroke-width="0"/><path d="M138.01,41.62h-2.32v6.29h-4.28v-17.77h7c2.11,0,3.75.47,4.94,1.4,1.19.93,1.78,2.26,1.78,3.96,0,1.24-.25,2.26-.75,3.07-.5.81-1.28,1.47-2.35,1.98l3.71,7.18v.18h-4.59l-3.14-6.29ZM135.69,38.33h2.71c.81,0,1.43-.21,1.84-.64.41-.43.62-1.03.62-1.79s-.21-1.37-.62-1.81c-.41-.44-1.03-.65-1.83-.65h-2.71v4.9Z" fill="#fff" stroke-width="0"/></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,6 @@
---
title: "git"
quadrant: tools
ring: adopt
tags: []
---

View File

@@ -0,0 +1,6 @@
---
title: "gitea"
quadrant: tools
ring: assess
tags: []
---

View File

@@ -0,0 +1,6 @@
---
title: "node.js"
quadrant: languages-and-frameworks
ring: hold
tags: [iac]
---

View File

@@ -0,0 +1,6 @@
---
title: "opentofu"
quadrant: platforms-and-operations
ring: trial
tags: [iac]
---

View File

@@ -0,0 +1,6 @@
---
title: "pulumi"
quadrant: platforms-and-operations
ring: adopt
tags: [iac]
---

View File

@@ -0,0 +1,6 @@
---
title: "terraform"
quadrant: platforms-and-operations
ring: adopt
tags: [iac]
---

View File

@@ -0,0 +1,13 @@
# Tech radar
It **is** [Zalando's tech radar], just customized.
Serves a static page.
There are **4** quadrants.<br/>
Quadrants start from the **bottom right** and go clockwise: `0` is _bottom right_, `1` is _bottom left_, `2` is
_top left_, `3` is _top right_.
Moved: `-1` is _down_, `0` is _stationary_, `1` is _up_.
[zalando's tech radar]: https://github.com/zalando/tech-radar

View File

@@ -0,0 +1,13 @@
---
version: '3'
services:
radar:
container_name: radar
image: nginxinc/nginx-unprivileged:1.25.5-bookworm-perl
volumes:
- ${PWD}:/usr/share/nginx/html:ro
ports:
- 8080:8080
environment:
NGINX_HOST: localhost
NGINX_PORT: '8080'

View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="description"
content="Tech Radar: a tool to visualize technology choices, inspire and support Engineering teams at Example to pick the best technologies for new projects">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Tech Radar</title>
<link rel="shortcut icon"
href="view-source:https://images.squarespace-cdn.com/content/v1/6414395791ffc20447907be9/94d4e41d-9d48-4068-89a1-52f0a7e36890/favicon.ico?format=100w">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="radar.js"></script>
<link rel="stylesheet" href="radar.css">
</head>
<body>
<svg id="radar"></svg>
<script>
fetch('./radar.json').then(function (response) {
return response.json();
}).then(function (data) {
radar_visualization({
svg_id: "radar",
width: 1450,
height: 950,
colors: {
background: "#fff",
grid: '#dddde0',
inactive: "#ddd"
},
title: "Tech Radar",
date: data.date,
quadrants: [
{ name: "Data Management" }, // BR, #0
{ name: "Datastores" }, // BL, #1
{ name: "Languages" }, // TR, #2
{ name: "Infrastructure" }, // TL, #3
],
rings: [
{ name: "ADOPT", color: "#5ba300" },
{ name: "TRIAL", color: "#009eb0" },
{ name: "ASSESS", color: "#c7ba00" },
{ name: "HOLD", color: "#e09b96" }
],
print_layout: true,
links_in_new_tabs: true,
// zoomed_quadrant: 0,
entries: data.entries
});
}).catch(function (err) {
console.log('Error loading config.json', err);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,23 @@
body {
font-family: 'Source Sans Pro', arial, helvetica, sans-serif;
padding-bottom: 50px;
}
h3 {
margin-top: 50px;
}
li {
margin: 25px 50px 0 0;
}
table {
width: 1400px;
margin: 0 50px 0 50px;
}
td {
width: 50%;
vertical-align: top;
padding-right: 60px;
}

View File

@@ -0,0 +1,474 @@
// The MIT License (MIT)
// Copyright (c) 2017-2024 Zalando SE
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
function radar_visualization(config) {
// custom random number generator, to make random sequence reproducible
// source: https://stackoverflow.com/questions/521295
var seed = 42;
function random() {
var x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
function random_between(min, max) {
return min + random() * (max - min);
}
function normal_between(min, max) {
return min + (random() + random()) * 0.5 * (max - min);
}
// radial_min / radial_max are multiples of PI
const quadrants = [
{ radial_min: 0, radial_max: 0.5, factor_x: 1, factor_y: 1 },
{ radial_min: 0.5, radial_max: 1, factor_x: -1, factor_y: 1 },
{ radial_min: -1, radial_max: -0.5, factor_x: -1, factor_y: -1 },
{ radial_min: -0.5, radial_max: 0, factor_x: 1, factor_y: -1 }
];
const rings = [
{ radius: 130 },
{ radius: 220 },
{ radius: 310 },
{ radius: 400 }
];
const title_offset =
{ x: -675, y: -420 };
const footer_offset =
{ x: -675, y: 420 };
const legend_offset = [
{ x: 450, y: 90 },
{ x: -675, y: 90 },
{ x: -675, y: -310 },
{ x: 450, y: -310 }
];
function polar(cartesian) {
var x = cartesian.x;
var y = cartesian.y;
return {
t: Math.atan2(y, x),
r: Math.sqrt(x * x + y * y)
}
}
function cartesian(polar) {
return {
x: polar.r * Math.cos(polar.t),
y: polar.r * Math.sin(polar.t)
}
}
function bounded_interval(value, min, max) {
var low = Math.min(min, max);
var high = Math.max(min, max);
return Math.min(Math.max(value, low), high);
}
function bounded_ring(polar, r_min, r_max) {
return {
t: polar.t,
r: bounded_interval(polar.r, r_min, r_max)
}
}
function bounded_box(point, min, max) {
return {
x: bounded_interval(point.x, min.x, max.x),
y: bounded_interval(point.y, min.y, max.y)
}
}
function segment(quadrant, ring) {
var polar_min = {
t: quadrants[quadrant].radial_min * Math.PI,
r: ring === 0 ? 30 : rings[ring - 1].radius
};
var polar_max = {
t: quadrants[quadrant].radial_max * Math.PI,
r: rings[ring].radius
};
var cartesian_min = {
x: 15 * quadrants[quadrant].factor_x,
y: 15 * quadrants[quadrant].factor_y
};
var cartesian_max = {
x: rings[3].radius * quadrants[quadrant].factor_x,
y: rings[3].radius * quadrants[quadrant].factor_y
};
return {
clipx: function(d) {
var c = bounded_box(d, cartesian_min, cartesian_max);
var p = bounded_ring(polar(c), polar_min.r + 15, polar_max.r - 15);
d.x = cartesian(p).x; // adjust data too!
return d.x;
},
clipy: function(d) {
var c = bounded_box(d, cartesian_min, cartesian_max);
var p = bounded_ring(polar(c), polar_min.r + 15, polar_max.r - 15);
d.y = cartesian(p).y; // adjust data too!
return d.y;
},
random: function() {
return cartesian({
t: random_between(polar_min.t, polar_max.t),
r: normal_between(polar_min.r, polar_max.r)
});
}
}
}
// position each entry randomly in its segment
for (var i = 0; i < config.entries.length; i++) {
var entry = config.entries[i];
entry.segment = segment(entry.quadrant, entry.ring);
var point = entry.segment.random();
entry.x = point.x;
entry.y = point.y;
entry.color = entry.active || config.print_layout ?
config.rings[entry.ring].color : config.colors.inactive;
}
// partition entries according to segments
var segmented = new Array(4);
for (var quadrant = 0; quadrant < 4; quadrant++) {
segmented[quadrant] = new Array(4);
for (var ring = 0; ring < 4; ring++) {
segmented[quadrant][ring] = [];
}
}
for (var i=0; i<config.entries.length; i++) {
var entry = config.entries[i];
segmented[entry.quadrant][entry.ring].push(entry);
}
// assign unique sequential id to each entry
var id = 1;
for (var quadrant of [2,3,1,0]) {
for (var ring = 0; ring < 4; ring++) {
var entries = segmented[quadrant][ring];
entries.sort(function(a,b) { return a.label.localeCompare(b.label); })
for (var i=0; i<entries.length; i++) {
entries[i].id = "" + id++;
}
}
}
function translate(x, y) {
return "translate(" + x + "," + y + ")";
}
function viewbox(quadrant) {
return [
Math.max(0, quadrants[quadrant].factor_x * 400) - 420,
Math.max(0, quadrants[quadrant].factor_y * 400) - 420,
440,
440
].join(" ");
}
// adjust with config.scale.
config.scale = config.scale || 1;
var scaled_width = config.width * config.scale;
var scaled_height = config.height * config.scale;
var svg = d3.select("svg#" + config.svg_id)
.style("background-color", config.colors.background)
.attr("width", scaled_width)
.attr("height", scaled_height);
var radar = svg.append("g");
if ("zoomed_quadrant" in config) {
svg.attr("viewBox", viewbox(config.zoomed_quadrant));
} else {
radar.attr("transform", translate(scaled_width / 2, scaled_height / 2).concat(`scale(${config.scale})`));
}
var grid = radar.append("g");
// draw grid lines
grid.append("line")
.attr("x1", 0).attr("y1", -400)
.attr("x2", 0).attr("y2", 400)
.style("stroke", config.colors.grid)
.style("stroke-width", 1);
grid.append("line")
.attr("x1", -400).attr("y1", 0)
.attr("x2", 400).attr("y2", 0)
.style("stroke", config.colors.grid)
.style("stroke-width", 1);
// background color. Usage `.attr("filter", "url(#solid)")`
// SOURCE: https://stackoverflow.com/a/31013492/2609980
var defs = grid.append("defs");
var filter = defs.append("filter")
.attr("x", 0)
.attr("y", 0)
.attr("width", 1)
.attr("height", 1)
.attr("id", "solid");
filter.append("feFlood")
.attr("flood-color", "rgb(0, 0, 0, 0.8)");
filter.append("feComposite")
.attr("in", "SourceGraphic");
// draw rings
for (var i = 0; i < rings.length; i++) {
grid.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", rings[i].radius)
.style("fill", "none")
.style("stroke", config.colors.grid)
.style("stroke-width", 1);
if (config.print_layout) {
grid.append("text")
.text(config.rings[i].name)
.attr("y", -rings[i].radius + 62)
.attr("text-anchor", "middle")
.style("fill", config.rings[i].color)
.style("opacity", 0.35)
.style("font-family", "Arial, Helvetica")
.style("font-size", "42px")
.style("font-weight", "bold")
.style("pointer-events", "none")
.style("user-select", "none");
}
}
function legend_transform(quadrant, ring, index=null) {
var dx = ring < 2 ? 0 : 140;
var dy = (index == null ? -16 : index * 12);
if (ring % 2 === 1) {
dy = dy + 36 + segmented[quadrant][ring-1].length * 12;
}
return translate(
legend_offset[quadrant].x + dx,
legend_offset[quadrant].y + dy
);
}
// draw title and legend (only in print layout)
if (config.print_layout) {
// title
radar.append("text")
.attr("transform", translate(title_offset.x, title_offset.y))
.text(config.title)
.style("font-family", "Arial, Helvetica")
.style("font-size", "30")
.style("font-weight", "bold")
// date
radar
.append("text")
.attr("transform", translate(title_offset.x, title_offset.y + 20))
.text(config.date || "")
.style("font-family", "Arial, Helvetica")
.style("font-size", "14")
.style("fill", "#999")
// footer
radar.append("text")
.attr("transform", translate(footer_offset.x, footer_offset.y))
.text("▲ moved up ▼ moved down")
.attr("xml:space", "preserve")
.style("font-family", "Arial, Helvetica")
.style("font-size", "10px");
// legend
var legend = radar.append("g");
for (var quadrant = 0; quadrant < 4; quadrant++) {
legend.append("text")
.attr("transform", translate(
legend_offset[quadrant].x,
legend_offset[quadrant].y - 45
))
.text(config.quadrants[quadrant].name)
.style("font-family", "Arial, Helvetica")
.style("font-size", "18px")
.style("font-weight", "bold");
for (var ring = 0; ring < 4; ring++) {
legend.append("text")
.attr("transform", legend_transform(quadrant, ring))
.text(config.rings[ring].name)
.style("font-family", "Arial, Helvetica")
.style("font-size", "12px")
.style("font-weight", "bold")
.style("fill", config.rings[ring].color);
legend.selectAll(".legend" + quadrant + ring)
.data(segmented[quadrant][ring])
.enter()
.append("a")
.attr("href", function (d, i) {
return d.link ? d.link : "#"; // stay on same page if no link was provided
})
// Add a target if (and only if) there is a link and we want new tabs
.attr("target", function (d, i) {
return (d.link && config.links_in_new_tabs) ? "_blank" : null;
})
.append("text")
.attr("transform", function(d, i) { return legend_transform(quadrant, ring, i); })
.attr("class", "legend" + quadrant + ring)
.attr("id", function(d, i) { return "legendItem" + d.id; })
.text(function(d, i) { return d.id + ". " + d.label; })
.style("font-family", "Arial, Helvetica")
.style("font-size", "11px")
.on("mouseover", function(d) { showBubble(d); highlightLegendItem(d); })
.on("mouseout", function(d) { hideBubble(d); unhighlightLegendItem(d); });
}
}
}
// layer for entries
var rink = radar.append("g")
.attr("id", "rink");
// rollover bubble (on top of everything else)
var bubble = radar.append("g")
.attr("id", "bubble")
.attr("x", 0)
.attr("y", 0)
.style("opacity", 0)
.style("pointer-events", "none")
.style("user-select", "none");
bubble.append("rect")
.attr("rx", 4)
.attr("ry", 4)
.style("fill", "#333");
bubble.append("text")
.style("font-family", "sans-serif")
.style("font-size", "10px")
.style("fill", "#fff");
bubble.append("path")
.attr("d", "M 0,0 10,0 5,8 z")
.style("fill", "#333");
function showBubble(d) {
if (d.active || config.print_layout) {
var tooltip = d3.select("#bubble text")
.text(d.label);
var bbox = tooltip.node().getBBox();
d3.select("#bubble")
.attr("transform", translate(d.x - bbox.width / 2, d.y - 16))
.style("opacity", 0.8);
d3.select("#bubble rect")
.attr("x", -5)
.attr("y", -bbox.height)
.attr("width", bbox.width + 10)
.attr("height", bbox.height + 4);
d3.select("#bubble path")
.attr("transform", translate(bbox.width / 2 - 5, 3));
}
}
function hideBubble(d) {
var bubble = d3.select("#bubble")
.attr("transform", translate(0,0))
.style("opacity", 0);
}
function highlightLegendItem(d) {
var legendItem = document.getElementById("legendItem" + d.id);
legendItem.setAttribute("filter", "url(#solid)");
legendItem.setAttribute("fill", "white");
}
function unhighlightLegendItem(d) {
var legendItem = document.getElementById("legendItem" + d.id);
legendItem.removeAttribute("filter");
legendItem.removeAttribute("fill");
}
// draw blips on radar
var blips = rink.selectAll(".blip")
.data(config.entries)
.enter()
.append("g")
.attr("class", "blip")
.attr("transform", function(d, i) { return legend_transform(d.quadrant, d.ring, i); })
.on("mouseover", function(d) { showBubble(d); highlightLegendItem(d); })
.on("mouseout", function(d) { hideBubble(d); unhighlightLegendItem(d); });
// configure each blip
blips.each(function(d) {
var blip = d3.select(this);
// blip link
if (d.active && Object.prototype.hasOwnProperty.call(d, "link") && d.link) {
blip = blip.append("a")
.attr("xlink:href", d.link);
if (config.links_in_new_tabs) {
blip.attr("target", "_blank");
}
}
// blip shape
if (d.moved > 0) {
blip.append("path")
.attr("d", "M -11,5 11,5 0,-13 z") // triangle pointing up
.style("fill", d.color);
} else if (d.moved < 0) {
blip.append("path")
.attr("d", "M -11,-5 11,-5 0,13 z") // triangle pointing down
.style("fill", d.color);
} else {
blip.append("circle")
.attr("r", 9)
.attr("fill", d.color);
}
// blip text
if (d.active || config.print_layout) {
var blip_text = config.print_layout ? d.id : d.label.match(/[a-z]/i);
blip.append("text")
.text(blip_text)
.attr("y", 3)
.attr("text-anchor", "middle")
.style("fill", "#fff")
.style("font-family", "Arial, Helvetica")
.style("font-size", function(d) { return blip_text.length > 2 ? "8px" : "9px"; })
.style("pointer-events", "none")
.style("user-select", "none");
}
});
// make sure that blips stay inside their segment
function ticked() {
blips.attr("transform", function(d) {
return translate(d.segment.clipx(d), d.segment.clipy(d));
})
}
// distribute blips, while avoiding collisions
d3.forceSimulation()
.nodes(config.entries)
.velocityDecay(0.19) // magic number (found by experimentation)
.force("collision", d3.forceCollide().radius(12).strength(0.85))
.on("tick", ticked);
}

View File

@@ -0,0 +1,41 @@
{
"date": "2024.04.25",
"entries": [
{
"quadrant": 2,
"ring": 0,
"label": "node.js",
"active": true,
"moved": 0
},
{
"quadrant": 3,
"ring": 0,
"label": "pulumi",
"link": "https://www.pulumi.com/",
"active": true,
"moved": 0
},
{
"quadrant": 2,
"ring": 1,
"label": "python",
"active": true,
"moved": 0
},
{
"quadrant": 2,
"ring": 2,
"label": "golang",
"active": true,
"moved": -1
},
{
"quadrant": 1,
"ring": 3,
"label": "gitea",
"active": false,
"moved": 1
}
]
}