Skip to content

Commit

Permalink
feat: add rating from https:/phodal/powermd
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Mar 11, 2020
1 parent 4aee7f6 commit 8ed4116
Show file tree
Hide file tree
Showing 22 changed files with 1,065 additions and 8 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
"@angular/platform-browser-dynamic": "~9.0.3",
"@angular/router": "~9.0.3",
"@ngx-pwa/local-storage": "^9.0.2",
"d3": "^3.5.17",
"lodash-es": "^4.17.15",
"marked": "^0.8.0",
"ng2-dragula": "^2.1.1",
"ngx-markdown": "^9.0.0",
"rxjs": "~6.5.4",
"shortid": "^2.2.15",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
},
Expand All @@ -35,6 +38,7 @@
"@angular/cli": "~9.0.4",
"@angular/compiler-cli": "~9.0.3",
"@angular/language-service": "~9.0.3",
"@types/d3": "^3.5.17",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
Expand Down
258 changes: 258 additions & 0 deletions src/app/shared/components/markdown-radar-chart/RadarChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
//Practically all this code comes from https:/alangrafu/radar-chart-d3
//I only made some additions and aesthetic adjustments to make the chart look better
//(of course, that is only my point of view)
//Such as a better placement of the titles at each line end,
//adding numbers that reflect what each circular level stands for
//Not placing the last level and slight differences in color
//
//For a bit of extra information check the blog about it:
//http://nbremer.blogspot.nl/2013/09/making-d3-radar-chart-look-bit-better.html

import d3 from "d3";

var RadarChart = {
draw: function (id, chartData, options) {
var cfg = {
radius: 5,
w: options.width,
h: options.height,
factor: 1,
factorLegend: 1,
levels: 3,
maxValue: 0,
radians: 2 * Math.PI,
opacityArea: 0.2,
ToRight: 5,
TranslateX: 150,
TranslateY: 90,
ExtraWidthX: 100,
ExtraWidthY: 200,
color: d3.scale.category10()
};

if ('undefined' !== typeof options) {
for (var i in options) {
if ('undefined' !== typeof options[i]) {
cfg[i] = options[i];
}
}
}
var allAxis = (chartData[0].map(function (i, j) {
return i.axis
}));
var total = allAxis.length;
var radius = cfg.factor * Math.min(cfg.w / 2, cfg.h / 2);
var Format = d3.format('');
d3.select(id).select("svg").remove();

var g = d3.select(id)
.append("svg")
.attr("width", cfg.w + cfg.ExtraWidthX)
.attr("height", cfg.h + cfg.ExtraWidthY)
.append("g")
.attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")");

var tooltip;

//Circular segments
for (var j = 0; j < cfg.levels; j++) {
var levelFactor = cfg.factor * radius * ((j + 1) / cfg.levels);
g.selectAll(".levels")
.data(allAxis)
.enter()
.append("svg:line")
.attr("x1", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.sin(i * cfg.radians / total));
})
.attr("y1", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.cos(i * cfg.radians / total));
})
.attr("x2", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.sin((i + 1) * cfg.radians / total));
})
.attr("y2", function (d, i) {
return levelFactor * (1 - cfg.factor * Math.cos((i + 1) * cfg.radians / total));
})
.attr("class", "line")
.style("stroke", "grey")
.style("stroke-opacity", "0.75")
.style("stroke-width", "0.3px")
.attr("transform", "translate(" + (cfg.w / 2 - levelFactor) + ", " + (cfg.h / 2 - levelFactor) + ")");
}

//Text indicating at what % each level is
for (var j = 0; j < cfg.levels; j++) {
var levelFactor = cfg.factor * radius * ((j + 1) / cfg.levels);
g.selectAll(".levels")
.data([1]) //dummy data
.enter()
.append("svg:text")
.attr("x", function (d) {
return levelFactor * (1 - cfg.factor * Math.sin(0));
})
.attr("y", function (d) {
return levelFactor * (1 - cfg.factor * Math.cos(0));
})
.attr("class", "legend")
.style("font-family", "sans-serif")
.style("font-size", "10px")
.attr("transform", "translate(" + (cfg.w / 2 - levelFactor + cfg.ToRight) + ", " + (cfg.h / 2 - levelFactor) + ")")
.attr("fill", "#737373")
.text(Format((j + 1) * cfg.maxValue / cfg.levels));
}

var chart0Length = chartData[0].length;

var series = 0;

var axis = g.selectAll(".axis")
.data(allAxis)
.enter()
.append("g")
.attr("class", "axis");

axis.append("line")
.attr("x1", cfg.w / 2)
.attr("y1", cfg.h / 2)
.attr("x2", function (d, i) {
return cfg.w / 2 * (1 - cfg.factor * Math.sin(i * cfg.radians / total));
})
.attr("y2", function (d, i) {
return cfg.h / 2 * (1 - cfg.factor * Math.cos(i * cfg.radians / total));
})
.attr("class", "line")
.style("stroke", "grey")
.style("stroke-width", "1px");

axis.append("text")
.attr("class", "legend")
.text(function (d) {
return d
})
.style("font-family", "sans-serif")
.style("font-size", "14px")
.attr("text-anchor", "middle")
.attr("dy", "1.5em")
.attr("transform", function (d, i) {
return "translate(0, -10)"
})
.attr("x", function (d, i) {
return cfg.w / 2 * (1 - cfg.factorLegend * Math.sin((chart0Length - i) * cfg.radians / total)) - 80 * Math.sin((chart0Length - i) * cfg.radians / total);
})
.attr("y", function (d, i) {
return cfg.h / 2 * (1 - Math.cos(i * cfg.radians / total)) - 20 * Math.cos(i * cfg.radians / total);
});

var dataValues = [];
chartData.forEach(function (y, x) {
g.selectAll(".nodes")
.data(y, function (j, i) {
dataValues.push([
cfg.w / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.sin((chart0Length - i) * cfg.radians / total)),
cfg.h / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.cos((chart0Length - i) * cfg.radians / total))
]);
});
dataValues.push(dataValues[0]);
g.selectAll(".area")
.data([dataValues])
.enter()
.append("polygon")
.attr("class", "radar-chart-serie" + series)
.style("stroke-width", "3px")
.style("stroke", cfg.color(series))
.attr("points", function (d) {
var str = "";
for (var pti = 0; pti < d.length; pti++) {
str = str + d[pti][0] + "," + d[pti][1] + " ";
}
return str;
})
.style("fill", function (j, i) {
return cfg.color(series)
})
.style("fill-opacity", cfg.opacityArea)
.on('mouseover', function (d) {
var z = "polygon." + d3.select(this).attr("class");
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", 0.1);
g.selectAll(z)
.transition(200)
.style("fill-opacity", .4);
})
.on('mouseout', function () {
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", cfg.opacityArea);
});
series++;
});
series = 0;


chartData.forEach(function (y, x) {
g.selectAll(".nodes")
.data(y).enter()
.append("svg:circle")
.attr("class", "radar-chart-serie" + series)
.attr('r', cfg.radius)
.attr("alt", function (j) {
return Math.max(j.value, 0)
})
.attr("cx", function (j, i) {
dataValues.push([
cfg.w / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.sin((chart0Length - i) * cfg.radians / total)),
cfg.h / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.cos((chart0Length - i) * cfg.radians / total))
]);
return cfg.w / 2 * (1 - (Math.max(j.value, 0) / cfg.maxValue) * cfg.factor * Math.sin((chart0Length - i) * cfg.radians / total));
})
.attr("cy", function (j, i) {
return cfg.h / 2 * (1 - (Math.max(j.value, 0) / cfg.maxValue) * cfg.factor * Math.cos((chart0Length - i) * cfg.radians / total));
})
.attr("data-id", function (j) {
return j.axis
})
.style("fill", cfg.color(series)).style("fill-opacity", .9)
.on('mouseover', function (d) {
var newX = parseFloat(d3.select(this).attr('cx')) - 10;
var newY = parseFloat(d3.select(this).attr('cy')) - 5;

tooltip
.attr('x', newX)
.attr('y', newY)
.text(Format(d.value))
.transition(200)
.style('opacity', 1);

var z = "polygon." + d3.select(this).attr("class");
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", 0.1);
g.selectAll(z)
.transition(200)
.style("fill-opacity", .7);
})
.on('mouseout', function () {
tooltip
.transition(200)
.style('opacity', 0);
g.selectAll("polygon")
.transition(200)
.style("fill-opacity", cfg.opacityArea);
})
.append("svg:title")
.text(function (j) {
return Math.max(j.value, 0)
});

series++;
});
//Tooltip
tooltip = g.append('text')
.style('opacity', 0)
.style('font-family', 'sans-serif')
.style('font-size', '1.2em');
}
};

export default RadarChart;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div #baseElement id="markdown-radar">
<div id="chart"></div>
<markdown-rating
id="rating"
[(ngModel)]="value"
[isParent]="true"
(ngModelChange)="updateModel($event)">
</markdown-rating>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#markdown-radar {
display: flex;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { MarkdownRadarChartComponent } from './markdown-radar-chart.component';
import { SharedModule } from '../../shared.module';
import { FeaturesModule } from '../../../features/features.module';
import { MarkdownModule } from 'ngx-markdown';

describe('MarkdownRadarChartComponent', () => {
let component: MarkdownRadarChartComponent;
let fixture: ComponentFixture<MarkdownRadarChartComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SharedModule, FeaturesModule, MarkdownModule],
declarations: [ MarkdownRadarChartComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(MarkdownRadarChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 8ed4116

Please sign in to comment.