diff --git a/config_example.json b/config_example.json deleted file mode 100644 index 1116f72..0000000 --- a/config_example.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "quadrants": { - "languages-and-frameworks": "Languages & Frameworks", - "methods-and-patterns": "Methods & Patterns", - "platforms-and-aoe-services": "Platforms & Operations", - "tools": "Tools" - }, - "rings":["all", "adopt", "trial", "assess", "hold"] -} \ No newline at end of file diff --git a/dist_scripts/scripts/generateJson/radar.js b/dist_scripts/scripts/generateJson/radar.js index 5966310..1669a80 100644 --- a/dist_scripts/scripts/generateJson/radar.js +++ b/dist_scripts/scripts/generateJson/radar.js @@ -83,6 +83,7 @@ var front_matter_1 = __importDefault(require("front-matter")); var marked_1 = __importDefault(require("marked")); var highlight_js_1 = __importDefault(require("highlight.js")); var file_1 = require("./file"); +var model_1 = require("../../src/model"); var paths_1 = require("../paths"); marked_1.default.setOptions({ highlight: function (code) { return highlight_js_1.default.highlightAuto(code).value; }, @@ -180,7 +181,7 @@ var ignoreEmptyRevisionBody = function (revision, item) { }; var addRevisionToItem = function (item, revision) { if (item === void 0) { item = { - flag: "default", + flag: model_1.FlagType.default, featured: true, revisions: [], name: "", @@ -215,10 +216,10 @@ var hasItemChanged = function (item, allReleases) { }; var getItemFlag = function (item, allReleases) { if (isNewItem(item, allReleases)) { - return "new"; + return model_1.FlagType.new; } if (hasItemChanged(item, allReleases)) { - return "changed"; + return model_1.FlagType.changed; } - return "default"; + return model_1.FlagType.changed; }; diff --git a/dist_scripts/src/model.js b/dist_scripts/src/model.js index 615bc4d..3d97965 100644 --- a/dist_scripts/src/model.js +++ b/dist_scripts/src/model.js @@ -16,7 +16,19 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from) { return to; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.nonFeaturedOnly = exports.featuredOnly = void 0; +exports.getFirstLetter = exports.groupByFirstLetter = exports.groupByQuadrants = exports.nonFeaturedOnly = exports.featuredOnly = exports.FlagType = exports.HomepageOption = void 0; +var HomepageOption; +(function (HomepageOption) { + HomepageOption[HomepageOption["chart"] = 0] = "chart"; + HomepageOption[HomepageOption["columns"] = 1] = "columns"; + HomepageOption[HomepageOption["both"] = 2] = "both"; +})(HomepageOption = exports.HomepageOption || (exports.HomepageOption = {})); +var FlagType; +(function (FlagType) { + FlagType["new"] = "new"; + FlagType["changed"] = "changed"; + FlagType["default"] = "default"; +})(FlagType = exports.FlagType || (exports.FlagType = {})); var featuredOnly = function (items) { return items.filter(function (item) { return item.featured; }); }; diff --git a/package.json b/package.json index 691da9a..05785f8 100644 --- a/package.json +++ b/package.json @@ -24,15 +24,18 @@ "lint": "yarn ts:check && eslint src/**/*.tsx" }, "dependencies": { + "@types/d3": "^7.1.0", "@types/fs-extra": "^9.0.13", "@types/marked": "^2.0.3", "@types/node": "^16.10.1", "@types/react": "^17.0.24", "@types/react-dom": "^17.0.9", "@types/react-router-dom": "^5.3.0", + "@types/react-tooltip": "^4.2.4", "@types/sanitize-html": "^2.5.0", "@types/walk": "^2.3.1", "classnames": "^2.3.1", + "d3": "^7.2.1", "front-matter": "^4.0.2", "fs-extra": "^10.0.0", "highlight.js": "^11.2.0", @@ -44,6 +47,7 @@ "react-icons": "^4.2.0", "react-router-dom": "^5.3.0", "react-scripts": "4.0.3", + "react-tooltip": "^4.2.21", "sanitize-html": "^2.5.2", "sass": "^1.42.1", "typescript": "4.3.5", diff --git a/public/config.json b/public/config.json index 3f42d8a..e286e03 100644 --- a/public/config.json +++ b/public/config.json @@ -5,6 +5,64 @@ "platforms-and-aoe-services": "Platforms & Operations", "tools": "Tools" }, - "rings":["all", "adopt", "trial", "assess", "hold"], - "showEmptyRings": false -} + "rings": [ + "adopt", + "trial", + "assess", + "hold" + ], + "showEmptyRings": false, + "homepageContent": "both", + "quadrantsMap": { + "languages-and-frameworks": { + "colour": "#84BFA4", + "txtColour": "#444444", + "position": 1, + "description": "We've placed development languages (such as Scala or Golang) here, as well as more low-level development frameworks (such as Play or Symfony), which are useful for implementing custom software of all kinds." + }, + "methods-and-patterns": { + "colour": "#248EA6", + "txtColour": "white", + "position": 2, + "description": "Here we put information on methods and patterns concerning development, continuous x, testing, organization, architecture, etc." + }, + "platforms-and-aoe-services": { + "colour": "#F25244", + "txtColour": "#444444", + "position": 3, + "description": "Here we include infrastructure platforms and services. We also use this category to communicate news about AOE services that we want all AOE teams to be aware of." + }, + "tools": { + "colour": "#F2A25C", + "txtColour": "white", + "position": 4, + "description": "Here we put different software tools - from small helpers to bigger software projects" + } + }, + "chartConfig": { + "size": 800, + "scale": [ + -16, + 16 + ], + "blipSize": 12, + "ringsAttributes": [ + { + "radius": 8, + "arcWidth": 6 + }, + { + "radius": 11, + "arcWidth": 4 + }, + { + "radius": 14, + "arcWidth": 2 + }, + { + "radius": 16, + "arcWidth": 2 + } + ] + } +} \ No newline at end of file diff --git a/scripts/generateJson/radar.ts b/scripts/generateJson/radar.ts index 2af1431..14e571e 100644 --- a/scripts/generateJson/radar.ts +++ b/scripts/generateJson/radar.ts @@ -7,7 +7,7 @@ import marked from "marked"; import highlight from "highlight.js"; import { radarPath, getAllMarkdownFiles } from "./file"; -import { Item, Revision, ItemAttributes, Radar } from "../../src/model"; +import { Item, Revision, ItemAttributes, Radar, FlagType } from "../../src/model"; import { appBuild } from "../paths"; type FMAttributes = ItemAttributes; @@ -124,7 +124,7 @@ const ignoreEmptyRevisionBody = (revision: Revision, item: Item) => { const addRevisionToItem = ( item: Item = { - flag: "default", + flag: FlagType.default, featured: true, revisions: [], name: "", @@ -177,10 +177,10 @@ const hasItemChanged = (item: Item, allReleases: string[]) => const getItemFlag = (item: Item, allReleases: string[]): string => { if (isNewItem(item, allReleases)) { - return "new"; + return FlagType.new; } if (hasItemChanged(item, allReleases)) { - return "changed"; + return FlagType.changed; } - return "default"; + return FlagType.changed; }; diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx index db44899..79a97ca 100644 --- a/src/components/Badge/Badge.tsx +++ b/src/components/Badge/Badge.tsx @@ -1,6 +1,7 @@ import React, { MouseEventHandler } from "react"; import classNames from "classnames"; import "./badge.scss"; + type BadgeProps = { onClick?: MouseEventHandler; big?: boolean; diff --git a/src/components/Chart/Axes.tsx b/src/components/Chart/Axes.tsx new file mode 100644 index 0000000..2440721 --- /dev/null +++ b/src/components/Chart/Axes.tsx @@ -0,0 +1,46 @@ +import React, { useRef, useLayoutEffect } from 'react'; +import * as d3 from "d3"; + +export const YAxis: React.FC<{ + scale: d3.ScaleLinear +}> = ({ scale }) => { + + const ref = useRef(null); + + useLayoutEffect(() => { + if (ref.current == null) { + return + } + const axisGenerator = d3.axisLeft(scale).ticks(6); + d3.select(ref.current) + .attr('class', 'y-axis') + .call(axisGenerator) + .call(g => g.selectAll('.tick text').remove()) + .call(g => g.selectAll('.tick line').remove()) + .call(g => g.selectAll('.domain').remove()); + }, [scale]); + + return ; +}; + +export const XAxis: React.FC<{ + scale: d3.ScaleLinear +}> = ({ scale }) => { + + const ref = useRef(null); + + useLayoutEffect(() => { + if (ref.current == null) { + return + } + const axisGenerator = d3.axisBottom(scale).ticks(6); + d3.select(ref.current) + .attr('class', 'x-axis') + .call(axisGenerator) + .call(g => g.selectAll('.tick text').remove()) + .call(g => g.selectAll('.tick line').remove()) + .call(g => g.selectAll('.domain').remove()); + }, [scale]); + + return ; +}; diff --git a/src/components/Chart/BlipPoints.tsx b/src/components/Chart/BlipPoints.tsx new file mode 100644 index 0000000..f0fa82a --- /dev/null +++ b/src/components/Chart/BlipPoints.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import { ScaleLinear } from 'd3'; +import { FlagType, Item, Blip, Point } from '../../model'; +import Link from '../Link/Link'; +import { NewBlip, ChangedBlip, DefaultBlip } from './BlipShapes'; +import { ConfigData } from '../../config'; + +/* +See https://medium.com/create-code/build-a-radar-diagram-with-d3-js-9db6458a9248 +for a good explanation of formulas used to calculate various things in this component +*/ + +function generateCoordinates(blip: Blip, xScale: ScaleLinear, yScale: ScaleLinear, config: ConfigData): Point { + const pi = Math.PI, + ringRadius = config.chartConfig.ringsAttributes[blip.ringPosition].radius, + previousRingRadius = blip.ringPosition === 0 ? 0 : config.chartConfig.ringsAttributes[blip.ringPosition - 1].radius, + ringPadding = 0.7; + + // radian between 0 and 90 degrees + const randomDegree = ((Math.random() * 90) * pi) / 180; + // random distance from the centre of the radar, but within given ring. Also, with some "padding" so the points don't touch ring borders. + const radius = randomBetween(previousRingRadius + ringPadding, ringRadius - ringPadding); + /* + Multiples of PI/2. To apply the calculated position to the specific quadrant. + Order here is counter-clockwise, so we need to "invert" quadrant positions (i.e. swap 2 with 4) + */ + const shift = pi * [1, 4, 3, 2][blip.quadrantPosition - 1] / 2; + + return { + x: xScale(Math.cos(randomDegree + shift) * radius), + y: yScale(Math.sin(randomDegree + shift) * radius) + }; +}; + +function randomBetween (min: number, max: number): number { + return Math.random() * (max - min) + min; +}; + +function distanceBetween(point1: Point, point2: Point): number { + const a = point2.x - point1.x; + const b = point2.y - point1.y; + return Math.sqrt((a * a) + (b * b)); +}; + +function renderBlip(blip: Blip, index: number, config: ConfigData): JSX.Element { + const props = { + blip, + className: 'blip', + fill: blip.colour, + 'data-background-color': blip.colour, + 'data-text-color': blip.txtColour, + 'data-tip': blip.title, + key: index + } + switch (blip.flag) { + case FlagType.new: + return ; + case FlagType.changed: + return ; + default: + return ; + } +}; + +const BlipPoints: React.FC<{ + items: Item[] + xScale:ScaleLinear + yScale:ScaleLinear + config:ConfigData +}> = ({items, xScale, yScale, config}) => { + + const blips: Blip[] = items.reduce((list: Blip[], item: Item) => { + if (!item.ring || !item.quadrant) { + // skip the blip if it doesn't have a ring or quadrant assigned + return list; + } + const quadrantConfig = config.quadrantsMap[item.quadrant]; + if (!quadrantConfig) { + return list; + } + + let blip: Blip = { ...item, + quadrantPosition: quadrantConfig.position, + ringPosition: config.rings.findIndex(r => r === item.ring), + colour: quadrantConfig.colour, + txtColour: quadrantConfig.txtColour, + coordinates: {x: 0, y: 0}, + }; + + let point: Point; + let counter = 1; + let distanceBetweenCheck: boolean; + do { + const localpoint = generateCoordinates(blip, xScale, yScale, config); + point = localpoint + counter++; + /* + Generate position of the new blip until it has a satisfactory distance to every other blip (so that they don't touch each other) + and quadrant borders (so that they don't overlap quadrants) + This feels pretty inefficient, but good enough for now. + */ + distanceBetweenCheck = list.some(b => distanceBetween(localpoint, b.coordinates) < config.chartConfig.blipSize + config.chartConfig.blipSize / 2) + } while (counter < 100 + && (Math.abs(point.x - xScale(0)) < 15 + || Math.abs(point.y - yScale(0)) < 15 + || distanceBetweenCheck + )); + + blip.coordinates = point; + + list.push(blip); + return list; + }, []); + + return ( + + {blips.map((blip, index) => ( + + {renderBlip(blip, index, config)} + + ))} + + ); +}; + +export default BlipPoints; \ No newline at end of file diff --git a/src/components/Chart/BlipShapes.tsx b/src/components/Chart/BlipShapes.tsx new file mode 100644 index 0000000..47d8910 --- /dev/null +++ b/src/components/Chart/BlipShapes.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { ConfigData } from '../../config'; +import { Blip } from '../../model'; + +type VisualBlipProps = { + className: string, + fill: string, + 'data-background-color': string, + 'data-text-color': string, + 'data-tip': string, + key: number +} + +export const ChangedBlip: React.FC< + {blip: Blip, config: ConfigData} & VisualBlipProps +> = ({blip, config, ...props}) => { + const centeredX = blip.coordinates.x - config.chartConfig.blipSize/2, + centeredY = blip.coordinates.y - config.chartConfig.blipSize/2; + + return ( + + ); +}; + +export const NewBlip: React.FC< + {blip: Blip, config: ConfigData} & VisualBlipProps +> = ({blip, config, ...props}) => { + const centeredX = blip.coordinates.x - config.chartConfig.blipSize/2, + centeredY = blip.coordinates.y - config.chartConfig.blipSize/2; + + /* + The below is a predefined path of a triangle with rounded corners. + I didn't find any more human friendly way of doing this as all examples I found have tons of lines of code + e.g. https://observablehq.com/@perlmonger42/interactive-rounded-corners-on-svg-polygons-using-d3-js + */ + return ( + + ); +}; + +export const DefaultBlip: React.FC< + {blip: Blip, config: ConfigData} & VisualBlipProps +> = ({blip, config, ...props}) => { + return ( + + ); +}; \ No newline at end of file diff --git a/src/components/Chart/QuadrantRings.tsx b/src/components/Chart/QuadrantRings.tsx new file mode 100644 index 0000000..b4e6e2a --- /dev/null +++ b/src/components/Chart/QuadrantRings.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import * as d3 from 'd3'; +import { QuadrantConfig } from '../../model'; +import { ConfigData } from '../../config'; + +function arcPath(quadrantPosition: number, ringPosition: number, xScale: d3.ScaleLinear, config: ConfigData) { + const startAngle = quadrantPosition === 1 ? + 3 * Math.PI / 2 + : (quadrantPosition - 2) * Math.PI / 2 + const endAngle = quadrantPosition === 1 ? + 4 * Math.PI / 2 + : (quadrantPosition -1) * Math.PI / 2 + const arcAttrs = config.chartConfig.ringsAttributes[ringPosition], + ringRadiusPx = xScale(arcAttrs.radius) - xScale(0), + arc = d3.arc(); + + return arc({ + innerRadius: ringRadiusPx - arcAttrs.arcWidth, + outerRadius: ringRadiusPx, + startAngle, + endAngle + }) || undefined; +} + +const QuadrantRings: React.FC<{ + quadrant: QuadrantConfig + xScale: d3.ScaleLinear + config: ConfigData +}> = ({ quadrant, xScale, config}) => { + // order from top-right clockwise + const gradientAttributes = [ + {x: 0, y: 0, cx: 1, cy: 1, r: 1}, + {x: xScale(0), y: 0, cx: 0, cy: 1, r: 1}, + {x: xScale(0), y: xScale(0), cx: 0, cy: 0, r: 1}, + {x: 0, y: xScale(0), cx: 1, cy: 0, r: 1} + ]; + const gradientId = `${quadrant.position}-radial-gradient`, + quadrantSize = config.chartConfig.size / 2; + + return ( + + {/* Definition of the quadrant gradient */} + + + + + + + + {/* Gradient background area */} + + + {/* Rings' arcs */} + {Array.from(config.rings).map((ringPosition, index) => ( + + ))} + + + ); + } + + export default QuadrantRings; \ No newline at end of file diff --git a/src/components/Chart/RadarChart.tsx b/src/components/Chart/RadarChart.tsx new file mode 100644 index 0000000..031f2d6 --- /dev/null +++ b/src/components/Chart/RadarChart.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import * as d3 from "d3"; +import ReactTooltip from 'react-tooltip'; +import { Item } from '../../model'; +import { YAxis, XAxis } from './Axes'; +import QuadrantRings from './QuadrantRings'; +import BlipPoints from './BlipPoints'; + +import './chart.scss'; +import { ConfigData } from '../../config'; + +const RingLabel: React.FC<{ + ring: string + xScale: d3.ScaleLinear + yScale: d3.ScaleLinear + config: ConfigData +}> = ({ring, xScale, yScale, config}) => { + const ringIndex = config.rings.findIndex(r => r === ring) + + const ringRadius = config.chartConfig.ringsAttributes[ringIndex].radius, + previousRingRadius = ringIndex === 0 ? 0 : config.chartConfig.ringsAttributes[ringIndex - 1].radius, + + // middle point in between two ring arcs + distanceFromCentre = previousRingRadius + (ringRadius - previousRingRadius) / 2; + + return ( + + {/* Right hand-side label */} + + {ring} + + {/* Left hand-side label */} + + {ring} + + + ); +}; + +const RadarChart: React.FC<{ + items: Item[] + config: ConfigData +}> = ({ items, config }) => { + + const xScale = d3.scaleLinear() + .domain(config.chartConfig.scale) + .range([0, config.chartConfig.size]); + const yScale = d3.scaleLinear() + .domain(config.chartConfig.scale) + .range([config.chartConfig.size, 0]); + + return ( +
+ + + + + + + + + {Object.values(config.quadrantsMap).map((value, index) => ( + + ))} + + {Array.from(config.rings).map((ring: string, index) => ( + + ))} + + + + +
+ ); +} + +export default RadarChart; diff --git a/src/components/Chart/chart.scss b/src/components/Chart/chart.scss new file mode 100644 index 0000000..6ad6ae1 --- /dev/null +++ b/src/components/Chart/chart.scss @@ -0,0 +1,20 @@ +.chart { + fill: white; + font-size: 12px; + text-align: center; + position: relative; + margin: 0 auto; +} + +.chart .blip:hover { + cursor: pointer; +} + +.chart .tooltip { + padding: 5px 10px; + border-radius: 11px; +} + +.chart .ring-label { + text-transform: uppercase; +} \ No newline at end of file diff --git a/src/components/Flag/Flag.tsx b/src/components/Flag/Flag.tsx index 9d58a97..829254c 100644 --- a/src/components/Flag/Flag.tsx +++ b/src/components/Flag/Flag.tsx @@ -1,8 +1,6 @@ -import React from "react"; +import { FlagType } from "../../model"; import "./flag.scss"; -export type FlagType = "new" | "changed" | "default"; - interface ItemFlag { flag: FlagType; } @@ -16,7 +14,7 @@ export default function Flag({ }) { const ucFirst = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); - if (item.flag !== "default") { + if (item.flag !== FlagType.default) { let name = item.flag.toUpperCase(); let title = ucFirst(item.flag); if (short === true) { diff --git a/src/components/Item/testData.tsx b/src/components/Item/testData.tsx index 0cd0bd5..eb5eef8 100644 --- a/src/components/Item/testData.tsx +++ b/src/components/Item/testData.tsx @@ -1,7 +1,7 @@ -import { Item } from "../../model"; +import { FlagType, Item } from "../../model"; export const item: Item = { - flag: "default", + flag: FlagType.default, featured: false, revisions: [ { diff --git a/src/components/ItemRevision/ItemRevision.tsx b/src/components/ItemRevision/ItemRevision.tsx index 5661746..1096e22 100644 --- a/src/components/ItemRevision/ItemRevision.tsx +++ b/src/components/ItemRevision/ItemRevision.tsx @@ -1,4 +1,3 @@ -import React from "react"; import Badge from "../Badge/Badge"; import { formatRelease } from "../../date"; import { Revision } from "../../model"; diff --git a/src/components/Link/Link.tsx b/src/components/Link/Link.tsx index 6388705..32d0a7b 100644 --- a/src/components/Link/Link.tsx +++ b/src/components/Link/Link.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Link as RLink } from "react-router-dom"; import "./link.scss"; + type LinkProps = { pageName: string; style?: React.CSSProperties; diff --git a/src/components/PageHelp/PageHelp.tsx b/src/components/PageHelp/PageHelp.tsx index 3d2bacb..f967000 100644 --- a/src/components/PageHelp/PageHelp.tsx +++ b/src/components/PageHelp/PageHelp.tsx @@ -33,14 +33,13 @@ const PageHelp: React.FC = ({ leaving, onLeave }) => {

{headline}

{values.map((element, index) => { - const content = sanitizeHtml(element, { - allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li'], - allowedAttributes: { - 'a': ['href', 'target'] - }, - }); return ( -

+

) }) } diff --git a/src/components/PageIndex/PageIndex.tsx b/src/components/PageIndex/PageIndex.tsx index 69297d8..accb7ef 100644 --- a/src/components/PageIndex/PageIndex.tsx +++ b/src/components/PageIndex/PageIndex.tsx @@ -1,7 +1,8 @@ import { formatRelease } from "../../date"; -import { featuredOnly, Item } from "../../model"; +import { featuredOnly, Item, HomepageOption } from "../../model"; import HeroHeadline from "../HeroHeadline/HeroHeadline"; import QuadrantGrid from "../QuadrantGrid/QuadrantGrid"; +import RadarGrid from '../RadarGrid/RadarGrid'; import Fadeable from "../Fadeable/Fadeable"; import SetTitle from "../SetTitle"; import { ConfigData, radarName, radarNameShort } from "../../config"; @@ -28,6 +29,8 @@ export default function PageIndex({ const newestRelease = releases.slice(-1)[0]; const numberOfReleases = releases.length; + const showChart = config.homepageContent === HomepageOption.chart || config.homepageContent === HomepageOption.both; + const showColumns = config.homepageContent === HomepageOption.columns || config.homepageContent === HomepageOption.both; return ( @@ -36,7 +39,12 @@ export default function PageIndex({ {radarName} - + {showChart && ( + + )} + {showColumns && ( + + )}
{publishedLabel} {formatRelease(newestRelease)}
diff --git a/src/components/RadarGrid/RadarGrid.tsx b/src/components/RadarGrid/RadarGrid.tsx new file mode 100644 index 0000000..2b814a0 --- /dev/null +++ b/src/components/RadarGrid/RadarGrid.tsx @@ -0,0 +1,75 @@ +import React from "react"; +import RadarChart from "../Chart/RadarChart"; +import { ConfigData } from "../../config"; +import { Item, QuadrantConfig } from "../../model"; + +import "./radar-grid.scss"; +import Link from "../Link/Link"; + +const QuadrantLabel: React.FC<{ + quadrantConfig: QuadrantConfig; + quadrantName: string; + quadrantLabel: string; +}> = ({ quadrantConfig, quadrantName, quadrantLabel }) => { + const stylesMap = [ + { top: 0, left: 0 }, + { top: 0, right: 0 }, + { bottom: 0, right: 0 }, + { bottom: 0, left: 0 }, + ]; + + return ( +
+
+
+ Quadrant {quadrantConfig.position} +
+
+ + + Zoom In + +
+
+
+

{quadrantLabel}

+
{quadrantConfig.description}
+
+ ); +}; + +const Legend: React.FC = () => { + return ( +
+
+ + New in this version +
+
+ + Recently changed +
+
+ + Unchanged +
+
+ ); +}; + +const RadarGrid: React.FC<{ items: Item[]; config: ConfigData }> = ({ + items, + config, +}) => { + return ( +
+ + {Object.entries(config.quadrantsMap).map(([name, quadrant], index) => ( + + ))} + +
+ ); +}; + +export default RadarGrid; diff --git a/src/components/RadarGrid/radar-grid.scss b/src/components/RadarGrid/radar-grid.scss new file mode 100644 index 0000000..32e76f0 --- /dev/null +++ b/src/components/RadarGrid/radar-grid.scss @@ -0,0 +1,59 @@ +.radar-grid { + position: relative; + margin-bottom: 50px; + color: white; + display: none; +} + +@media screen and (min-width: 800px) { + .radar-grid { + display: block; + } +} + +.radar-grid .quadrant-label { + position: absolute; + width: 20%; +} + +.quadrant-label .split { + font-size: 12 px; + text-transform: uppercase; +} + +.quadrant-label hr { + width: 100%; + margin: 10px 0 10px 0; +} + +.quadrant-label .description { + font-size: 14px; + color: #a6b1bb +} + +.quadrant-label .icon-link { + font-size: 12px; +} + +.quadrant-label .icon-link .icon { + background-size: 18px 18px; + width: 18px; + height: 18px; +} + +.radar-grid .radar-legend { + position: absolute; + width: 15%; + right: 0; + top: 45%; +} + +.radar-legend .wrapper { + margin-bottom: 10px; +} + +.radar-legend .icon { + + background-position: center; + margin-right: 5px; +} \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index f1d357d..6cfcb61 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,9 +1,17 @@ -import { Item } from "./model"; +import {Item, HomepageOption, QuadrantConfig} from './model'; export interface ConfigData { quadrants: { [key: string]: string }; rings: string[]; showEmptyRings: boolean; + quadrantsMap: { [quadrant: string]: QuadrantConfig }; + chartConfig: { + size: number, + scale: number[], + blipSize: number, + ringsAttributes: {radius: number, arcWidth: number}[] + }; + homepageContent: HomepageOption; } export const radarName = diff --git a/src/icons/blip_changed.svg b/src/icons/blip_changed.svg new file mode 100644 index 0000000..d1dba9e --- /dev/null +++ b/src/icons/blip_changed.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/icons/blip_default.svg b/src/icons/blip_default.svg new file mode 100644 index 0000000..ea4537b --- /dev/null +++ b/src/icons/blip_default.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/icons/blip_new.svg b/src/icons/blip_new.svg new file mode 100644 index 0000000..ac6e180 --- /dev/null +++ b/src/icons/blip_new.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/model.ts b/src/model.ts index 9252890..25eced1 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,3 +1,9 @@ +export enum HomepageOption { + chart = "chart", + columns = "columns", + both = "both" +} + export type ItemAttributes = { name: string; ring: string; @@ -6,7 +12,11 @@ export type ItemAttributes = { featured?: boolean; }; -export type FlagType = "new" | "changed" | "default"; +export enum FlagType { + new = 'new', + changed = 'changed', + default = 'default' +} export type Item = ItemAttributes & { featured: boolean; @@ -16,6 +26,14 @@ export type Item = ItemAttributes & { revisions: Revision[]; }; +export type Blip = Item & { + quadrantPosition: number + ringPosition: number + colour: string + txtColour: string + coordinates: Point +} + export type Revision = ItemAttributes & { body: string; fileName: string; @@ -26,6 +44,13 @@ export type Quadrant = { [name: string]: Item[]; }; +export type QuadrantConfig = { + colour: string, + txtColour: string, + position: number, + description: string +} + export type Radar = { items: Item[]; releases: string[]; @@ -40,6 +65,11 @@ export const featuredOnly = (items: Item[]) => export const nonFeaturedOnly = (items: Item[]) => items.filter((item) => !item.featured); +export type Point = { + x: number, + y: number +} + export const groupByQuadrants = (items: Item[]): Group => items.reduce( (quadrants, item: Item) => ({ diff --git a/src/styles/components/icon.scss b/src/styles/components/icon.scss index 52cc610..c4f401c 100644 --- a/src/styles/components/icon.scss +++ b/src/styles/components/icon.scss @@ -30,4 +30,25 @@ &--close { background-image: url("../../icons/close.svg"); } + + &--blip_new { + background-image: url('../../icons/blip_new.svg'); + width: 18px; + height: 18px; + background-size: 18px; + } + + &--blip_changed { + background-image: url('../../icons/blip_changed.svg'); + width: 18px; + height: 18px; + background-size: 18px; + } + + &--blip_default { + background-image: url('../../icons/blip_default.svg'); + width: 18px; + height: 18px; + background-size: 18px; + } } diff --git a/yarn.lock b/yarn.lock index ec3ead7..1d53884 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1786,6 +1786,216 @@ dependencies: "@babel/types" "^7.3.0" +"@types/d3-array@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.2.tgz#71c35bca8366a40d1b8fce9279afa4a77fb0065d" + integrity sha512-5mjGjz6XOXKOCdTajXTZ/pMsg236RdiwKPrRPWAEf/2S/+PzwY+LLYShUpeysWaMvsdS7LArh6GdUefoxpchsQ== + +"@types/d3-axis@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-axis/-/d3-axis-3.0.1.tgz#6afc20744fa5cc0cbc3e2bd367b140a79ed3e7a8" + integrity sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-brush@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-brush/-/d3-brush-3.0.1.tgz#ae5f17ce391935ca88b29000e60ee20452c6357c" + integrity sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-chord@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-3.0.1.tgz#54c8856c19c8e4ab36a53f73ba737de4768ad248" + integrity sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw== + +"@types/d3-color@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.0.2.tgz#53f2d6325f66ee79afd707c05ac849e8ae0edbb0" + integrity sha512-WVx6zBiz4sWlboCy7TCgjeyHpNjMsoF36yaagny1uXfbadc9f+5BeBf7U+lRmQqY3EHbGQpP8UdW8AC+cywSwQ== + +"@types/d3-contour@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-contour/-/d3-contour-3.0.1.tgz#9ff4e2fd2a3910de9c5097270a7da8a6ef240017" + integrity sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ== + dependencies: + "@types/d3-array" "*" + "@types/geojson" "*" + +"@types/d3-delaunay@*": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-delaunay/-/d3-delaunay-6.0.0.tgz#c09953ac7e5460997f693d2d7bf3522e0d4a88e6" + integrity sha512-iGm7ZaGLq11RK3e69VeMM6Oqj2SjKUB9Qhcyd1zIcqn2uE8w9GFB445yCY46NOQO3ByaNyktX1DK+Etz7ZaX+w== + +"@types/d3-dispatch@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz#a1b18ae5fa055a6734cb3bd3cbc6260ef19676e3" + integrity sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw== + +"@types/d3-drag@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.1.tgz#fb1e3d5cceeee4d913caa59dedf55c94cb66e80f" + integrity sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-dsv@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.0.tgz#f3c61fb117bd493ec0e814856feb804a14cfc311" + integrity sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A== + +"@types/d3-ease@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.0.tgz#c29926f8b596f9dadaeca062a32a45365681eae0" + integrity sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA== + +"@types/d3-fetch@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-fetch/-/d3-fetch-3.0.1.tgz#f9fa88b81aa2eea5814f11aec82ecfddbd0b8fe0" + integrity sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw== + dependencies: + "@types/d3-dsv" "*" + +"@types/d3-force@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-force/-/d3-force-3.0.3.tgz#76cb20d04ae798afede1ea6e41750763ff5a9c82" + integrity sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA== + +"@types/d3-format@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-3.0.1.tgz#194f1317a499edd7e58766f96735bdc0216bb89d" + integrity sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg== + +"@types/d3-geo@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-geo/-/d3-geo-3.0.2.tgz#e7ec5f484c159b2c404c42d260e6d99d99f45d9a" + integrity sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ== + dependencies: + "@types/geojson" "*" + +"@types/d3-hierarchy@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-3.0.2.tgz#ca63f2f4da15b8f129c5b7dffd71d904cba6aca2" + integrity sha512-+krnrWOZ+aQB6v+E+jEkmkAx9HvsNAD+1LCD0vlBY3t+HwjKnsBFbpVLx6WWzDzCIuiTWdAxXMEnGnVXpB09qQ== + +"@types/d3-interpolate@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz#e7d17fa4a5830ad56fe22ce3b4fac8541a9572dc" + integrity sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.0.tgz#939e3a784ae4f80b1fde8098b91af1776ff1312b" + integrity sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg== + +"@types/d3-polygon@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-polygon/-/d3-polygon-3.0.0.tgz#5200a3fa793d7736fa104285fa19b0dbc2424b93" + integrity sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw== + +"@types/d3-quadtree@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz#433112a178eb7df123aab2ce11c67f51cafe8ff5" + integrity sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw== + +"@types/d3-random@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-random/-/d3-random-3.0.1.tgz#5c8d42b36cd4c80b92e5626a252f994ca6bfc953" + integrity sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ== + +"@types/d3-scale-chromatic@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#103124777e8cdec85b20b51fd3397c682ee1e954" + integrity sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw== + +"@types/d3-scale@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.2.tgz#41be241126af4630524ead9cb1008ab2f0f26e69" + integrity sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA== + dependencies: + "@types/d3-time" "*" + +"@types/d3-selection@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.2.tgz#23e48a285b24063630bbe312cc0cfe2276de4a59" + integrity sha512-d29EDd0iUBrRoKhPndhDY6U/PYxOWqgIZwKTooy2UkBfU7TNZNpRho0yLWPxlatQrFWk2mnTu71IZQ4+LRgKlQ== + +"@types/d3-shape@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.0.2.tgz#4b1ca4ddaac294e76b712429726d40365cd1e8ca" + integrity sha512-5+ButCmIfNX8id5seZ7jKj3igdcxx+S9IDBiT35fQGTLZUfkFgTv+oBH34xgeoWDKpWcMITSzBILWQtBoN5Piw== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time-format@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-4.0.0.tgz#ee7b6e798f8deb2d9640675f8811d0253aaa1946" + integrity sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw== + +"@types/d3-time@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" + integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== + +"@types/d3-timer@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.0.tgz#e2505f1c21ec08bda8915238e397fb71d2fc54ce" + integrity sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g== + +"@types/d3-transition@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.1.tgz#c9a96125567173d6163a6985b874f79154f4cc3d" + integrity sha512-Sv4qEI9uq3bnZwlOANvYK853zvpdKEm1yz9rcc8ZTsxvRklcs9Fx4YFuGA3gXoQN/c/1T6QkVNjhaRO/cWj94g== + dependencies: + "@types/d3-selection" "*" + +"@types/d3-zoom@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.1.tgz#4bfc7e29625c4f79df38e2c36de52ec3e9faf826" + integrity sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ== + dependencies: + "@types/d3-interpolate" "*" + "@types/d3-selection" "*" + +"@types/d3@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@types/d3/-/d3-7.1.0.tgz#8f32a7e7f434d8f920c8b1ebdfed55e18c033720" + integrity sha512-gYWvgeGjEl+zmF8c+U1RNIKqe7sfQwIXeLXO5Os72TjDjCEtgpvGBvZ8dXlAuSS1m6B90Y1Uo6Bm36OGR/OtCA== + dependencies: + "@types/d3-array" "*" + "@types/d3-axis" "*" + "@types/d3-brush" "*" + "@types/d3-chord" "*" + "@types/d3-color" "*" + "@types/d3-contour" "*" + "@types/d3-delaunay" "*" + "@types/d3-dispatch" "*" + "@types/d3-drag" "*" + "@types/d3-dsv" "*" + "@types/d3-ease" "*" + "@types/d3-fetch" "*" + "@types/d3-force" "*" + "@types/d3-format" "*" + "@types/d3-geo" "*" + "@types/d3-hierarchy" "*" + "@types/d3-interpolate" "*" + "@types/d3-path" "*" + "@types/d3-polygon" "*" + "@types/d3-quadtree" "*" + "@types/d3-random" "*" + "@types/d3-scale" "*" + "@types/d3-scale-chromatic" "*" + "@types/d3-selection" "*" + "@types/d3-shape" "*" + "@types/d3-time" "*" + "@types/d3-time-format" "*" + "@types/d3-timer" "*" + "@types/d3-transition" "*" + "@types/d3-zoom" "*" + "@types/eslint@^7.2.6": version "7.28.0" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.0.tgz#7e41f2481d301c68e14f483fe10b017753ce8d5a" @@ -1811,6 +2021,11 @@ dependencies: "@types/node" "*" +"@types/geojson@*": + version "7946.0.8" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" + integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== + "@types/glob@^7.1.1": version "7.1.4" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.4.tgz#ea59e21d2ee5c517914cb4bc8e4153b99e566672" @@ -1937,6 +2152,13 @@ "@types/history" "*" "@types/react" "*" +"@types/react-tooltip@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@types/react-tooltip/-/react-tooltip-4.2.4.tgz#d0acad2a3061806e10aab91dd26a95a77fd35125" + integrity sha512-UzjzmgY/VH3Str6DcAGTLMA1mVVhGOyARNTANExrirtp+JgxhaIOVDxq4TIRmpSi4voLv+w4HA9CC5GvhhCA0A== + dependencies: + react-tooltip "*" + "@types/react@*", "@types/react@^17.0.24": version "17.0.27" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.27.tgz#6498ed9b3ad117e818deb5525fa1946c09f2e0e6" @@ -3531,6 +3753,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@7, commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3541,11 +3768,6 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - common-tags@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" @@ -4060,6 +4282,250 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.1.1.tgz#7797eb53ead6b9083c75a45a681e93fc41bc468c" + integrity sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ== + dependencies: + internmap "1 - 2" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.0.1.tgz#03316e595955d1fcd39d9f3610ad41bb90194d0a" + integrity sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw== + +d3-contour@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-3.0.1.tgz#2c64255d43059599cd0dba8fe4cc3d51ccdd9bbd" + integrity sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ== + dependencies: + d3-array "2 - 3" + +d3-delaunay@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-6.0.2.tgz#7fd3717ad0eade2fc9939f4260acfb503f984e92" + integrity sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +d3-geo@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-3.0.1.tgz#4f92362fd8685d93e3b1fae0fd97dc8980b1ed7e" + integrity sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz#9cbb0ffd2375137a351e6cfeed344a06d4ff4597" + integrity sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +"d3-path@1 - 3", d3-path@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.0.1.tgz#f09dec0aaffd770b7995f1a399152bf93052321e" + integrity sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-scale-chromatic@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a" + integrity sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.0.1.tgz#9ccdfb28fd9b0d12f2d8aec234cd5c4a9ea27931" + integrity sha512-HNZNEQoDhuCrDWEc/BMbF/hKtzMZVoe64TvisFLDp2Iyj0UShB/E6/lBsLlJTfBMbYgftHj90cXJ0SEitlE6Xw== + dependencies: + d3-path "1 - 3" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.0.0.tgz#65972cb98ae2d4954ef5c932e8704061335d4975" + integrity sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3: + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.2.1.tgz#97eafaa6fc8cd7c564c3ace1e6678cbecf63f3ea" + integrity sha512-E/5sP0aeK6YPXI/+4QlefvBFgmcyR2jYftId0PrYWv4Y/gW3c3thp1XG4rQzF0eUwV9tR1x05X5eWuJ6rQXvew== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "3" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4195,6 +4661,13 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delaunator@5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-5.0.0.tgz#60f052b28bd91c9b4566850ebf7756efe821d81b" + integrity sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw== + dependencies: + robust-predicates "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5883,6 +6356,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" @@ -6033,6 +6513,11 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -9576,6 +10061,14 @@ react-scripts@4.0.3: optionalDependencies: fsevents "^2.1.3" +react-tooltip@*, react-tooltip@^4.2.21: + version "4.2.21" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f" + integrity sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig== + dependencies: + prop-types "^15.7.2" + uuid "^7.0.3" + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" @@ -9950,6 +10443,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +robust-predicates@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.1.tgz#ecde075044f7f30118682bd9fb3f123109577f9a" + integrity sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g== + rollup-plugin-babel@^4.3.3: version "4.4.0" resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.4.0.tgz#d15bd259466a9d1accbdb2fe2fff17c52d030acb" @@ -10004,6 +10502,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= + rxjs@^6.6.7: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -10028,7 +10531,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -11404,6 +11907,11 @@ uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"