diff --git a/src/charts/bubbleChart.ts b/src/charts/bubbleChart.ts index f560ab43b8ba18ed9f519d30a91e444a67f710a1..6993414716c51386c6ea605fa50bc62a50f7cd86 100644 --- a/src/charts/bubbleChart.ts +++ b/src/charts/bubbleChart.ts @@ -11,6 +11,7 @@ export type BubbleChartConfig = ChartConfig & { zoomExtent: [number, number], onZoom?: (event: any) => void, renderTooltip?: (dataPoint: any, tooltip: d3.Selection<d3.BaseType, unknown, HTMLElement, any>) => void, + onClick?: (dataPoint: any) => void, } export type BubbleChartConfigParam = ChartConfigParam & Partial<BubbleChartConfig> @@ -51,6 +52,8 @@ export default class BubbleChart extends SearchableChart { sizeAccessor: _config.sizeAccessor || (() => 5), colorAccessor: _config.colorAccessor || (() => null), zoomExtent: _config.zoomExtent || [0.5, 20], + renderTooltip: _config.renderTooltip || (() => null), + onClick: _config.onClick || (() => null), } } @@ -152,6 +155,9 @@ export default class BubbleChart extends SearchableChart { .on('mouseleave', (_: Event) => { d3.select('#tooltip').style('display', 'none'); }) + .on('click', (_: any, d: any) => { + vis.config.onClick?.(d.data) + }) } private renderGroupLabels() { diff --git a/src/charts/radarChart.ts b/src/charts/radarChart.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc431d7de62525666967a41a27e144cabe797e15 --- /dev/null +++ b/src/charts/radarChart.ts @@ -0,0 +1,93 @@ +import * as d3 from "d3"; +import {curveCardinalClosed, HierarchyNode, ScaleOrdinal, ScaleRadial, ScaleSequential} from "d3"; +import Chart, {ChartConfig, ChartConfigParam} from "@/charts/chart.ts"; + +export type RadarChartConfig = ChartConfig & { + renderTooltip?: (dataPoint: any, tooltip: d3.Selection<d3.BaseType, unknown, HTMLElement, any>) => void, + axisCircles: number, + attributes: string[], +} + +export type RadarChartConfigParam = ChartConfigParam & Partial<RadarChartConfig> + +export default class RadarChart extends Chart { + chartId: string = 'radarChart'; + chart: any + config: RadarChartConfig + colorScale: ScaleSequential<any> | ScaleOrdinal<any, any> | null = null + radialScale: ScaleRadial<any, any> = null + angleSlice = 0.7853981633974483 + + constructor(data: any[], _config: RadarChartConfigParam) { + super(data, _config as ChartConfigParam) + + this.config = this.createConfig(_config) + + this.initVis() + } + + private setConfig(_config: RadarChartConfigParam) { + this.config = this.createConfig(_config) + } + + private createConfig(_config: RadarChartConfigParam) { + return { + ..._config, + parentElement: typeof _config.parentElement === 'string' ? document.querySelector(_config.parentElement) as HTMLElement : _config.parentElement, + containerWidth: _config.containerWidth || 500, + containerHeight: _config.containerHeight || 140, + margin: _config.margin || {top: 10, bottom: 30, right: 10, left: 30}, + tooltipPadding: _config.tooltipPadding || 15, + axisCircles: _config.axisCircles || 2, + attributes: ['Per 90 Minutes _ G+A', 'Per 90 Minutes _ xG+xAG', 'PrgP', 'Total _ Cmp%', 'Tkl+Int', 'Touches _ Touches'], + } + } + + initVis() { + let vis = this; + vis.config.parentElement.innerHTML += ` + <svg id="${this.chartId}"></svg> + `; + vis.config.parentElement.innerHTML += ` + <div id="tooltip"></div> + `; + + const svg = d3.select(`#${this.chartId}`) + .attr('width', vis.config.containerWidth) + .attr('height', vis.config.containerHeight) + .attr('transform', `translate(${vis.config.margin.left},${vis.config.margin.top})`) + + vis.chart = svg.append('g') + + const domain = d3.range(0, 2 * Math.PI, vis.angleSlice) + + this.radialScale = d3.scaleLinear() + .domain(domain) + .range([0, 360]) + + vis.updateColorScale() + } + + updateVis(data: any[]): void { + this.data = data; + this.update() + } + + private update() { + let vis: RadarChart = this; + } + + renderVis(): void { + let vis: RadarChart = this; + + const node = vis.chart + .selectAll("g") + .data(vis.data) + .join("g") + + } + + updateColorScale() { + + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e4e7c3f64de1a3e60963fa5cee4cbce85a1588f1..35ee2976fbff3ba9a158d1a33e4e14d1e46c7fb5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,11 +5,13 @@ import Search from "@/search.ts"; import {countries} from "@/countries.ts"; import {numericColumns, Player} from "@/player.ts"; import {getPositionName} from "@/positions.ts"; +import RadarChart from "@/charts/radarChart.ts"; -// const radarChartWrapper = document.querySelector('#radar-chart-wrapper') as HTMLDivElement; +const radarChartWrapper = document.querySelector('#radar-chart-wrapper') as HTMLDivElement; const bubbleChartWrapper = document.querySelector('#bubble-chart-wrapper') as HTMLDivElement; const zoomExtent: [number, number] = [1, 5] let bubbleChart: BubbleChart | null = null +let radarChart: RadarChart | null = null let sliderBlocked = false const zoomSlider = document.querySelector('#zoom-slider') as HTMLInputElement @@ -55,6 +57,7 @@ sizeBySetting.oninput = (event) => { bubbleChart.updateVisConfig({sizeAccessor: (d: any) => d[event.target?.value] ?? 0} as BubbleChartConfigParam) } } +const selectedNodes: Player[] = [] dsv(';', 'data/output.csv').then(data => { const parsedData = data.map((d: any) => { @@ -94,6 +97,9 @@ dsv(';', 'data/output.csv').then(data => { <tr><th>Alter</th><td>${age[0]}${age[1] ? ' - ' + age[1] + ' Tage' : ''}</td></tr> </table> `) + }, + onClick: (dataPoint: any) => { + updateSelectedNodes(dataPoint) } }) @@ -113,4 +119,22 @@ function updateZoomLevel(to: '+' | '-') { if (bubbleChart) { to === '+' ? bubbleChart.zoomIn() : bubbleChart.zoomOut() } +} + +function updateSelectedNodes(node: Player) { + if (selectedNodes.some(n => n.player === node.player)) { + selectedNodes.splice(selectedNodes.findIndex((n: Player) => n.player === node.player), 1) + } else { + selectedNodes.push(node) + } + if (!radarChart) { + radarChart = new RadarChart(selectedNodes, { + parentElement: radarChartWrapper, + containerWidth: 500, + containerHeight: 500, + margin: {top: 20, right: 20, bottom: 20, left: 20}, + axisCircles: 2, + }) + radarChart.renderVis() + } } \ No newline at end of file