Initial Language Graph Implementation
This commit is contained in:
118
package-lock.json
generated
118
package-lock.json
generated
@@ -25,12 +25,14 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||
"@glidejs/glide": "^3.6.0",
|
||||
"apexcharts": "^3.45.1",
|
||||
"bootstrap": "^4.6.2",
|
||||
"cookieconsent": "^3.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"envsub": "^4.1.0",
|
||||
"express": "^4.18.1",
|
||||
"jquery": "^3.6.0",
|
||||
"ng-apexcharts": "^1.8.0",
|
||||
"ngx-cookie-service": "^16.0.1",
|
||||
"ngx-cookieconsent": "^4.0.2",
|
||||
"ngx-glide": "^16.0.0",
|
||||
@@ -5440,6 +5442,11 @@
|
||||
"node": ">=14.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@yr/monotone-cubic-spline": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
||||
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
|
||||
},
|
||||
"node_modules/@zkochan/js-yaml": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz",
|
||||
@@ -5722,6 +5729,20 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/apexcharts": {
|
||||
"version": "3.45.1",
|
||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.45.1.tgz",
|
||||
"integrity": "sha512-pPjj/SA6dfPvR/IKRZF0STdfBGpBh3WRt7K0DFuW9P8erypYkX17EHu3/molPRfo2zSiQwTVpshHC5ncysqfkA==",
|
||||
"dependencies": {
|
||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
||||
"svg.draggable.js": "^2.2.2",
|
||||
"svg.easing.js": "^2.0.0",
|
||||
"svg.filter.js": "^2.0.2",
|
||||
"svg.pathmorphing.js": "^0.1.3",
|
||||
"svg.resize.js": "^1.4.3",
|
||||
"svg.select.js": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/aproba": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||
@@ -12063,6 +12084,20 @@
|
||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/ng-apexcharts": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.8.0.tgz",
|
||||
"integrity": "sha512-NwJuMLHoLm52LSzM08RXV6oOOTyUYREAV53WHVGs+L2qi8UWbxCz19hX0kk+F/xFLEhhuiLegO3T1v30jLbKSQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": ">=13.0.0",
|
||||
"@angular/core": ">=13.0.0",
|
||||
"apexcharts": "^3.41.0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ngx-cookie-service": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-16.0.1.tgz",
|
||||
@@ -15233,6 +15268,89 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.easing.js": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
||||
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
||||
"dependencies": {
|
||||
"svg.js": ">=2.3.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.filter.js": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
||||
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||
},
|
||||
"node_modules/svg.pathmorphing.js": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-observable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||
|
||||
@@ -33,12 +33,14 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.1",
|
||||
"@glidejs/glide": "^3.6.0",
|
||||
"apexcharts": "^3.45.1",
|
||||
"bootstrap": "^4.6.2",
|
||||
"cookieconsent": "^3.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"envsub": "^4.1.0",
|
||||
"express": "^4.18.1",
|
||||
"jquery": "^3.6.0",
|
||||
"ng-apexcharts": "^1.8.0",
|
||||
"ngx-cookie-service": "^16.0.1",
|
||||
"ngx-cookieconsent": "^4.0.2",
|
||||
"ngx-glide": "^16.0.0",
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
}
|
||||
|
||||
.card .card-content {
|
||||
height: 350px;
|
||||
width: 100%;
|
||||
padding: 40px;
|
||||
display: flex;
|
||||
@@ -34,13 +33,9 @@
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
.card .card-content .card-content-f .card-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.card .card-content .card-content-f .card-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
@@ -72,3 +67,22 @@
|
||||
width: 100%;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
.card-languages {
|
||||
padding: 0;
|
||||
margin-top: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
/* COMPUTER FORMAT */
|
||||
@media only screen and (min-width: 712px) {
|
||||
.card-languages {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card .card-content .card-content-f .card-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,18 @@
|
||||
<p class="card-text">{{project.description}}</p>
|
||||
</div>
|
||||
<div class="card-content-f row">
|
||||
<div class="card-info col-9" *ngIf="hasLanguage">
|
||||
<p >Language: {{project.languages}}</p>
|
||||
<div class="card-languages col-sm-9" *ngIf="hasLanguage" id="language-chart">
|
||||
<apx-chart *ngIf="chartOptions !== undefined"
|
||||
[series]="chartOptions.series"
|
||||
[colors]="chartOptions.colors"
|
||||
[chart]="chartOptions.chart"
|
||||
[labels]="chartOptions.labels"
|
||||
[responsive]="chartOptions.responsive"
|
||||
[plotOptions]="chartOptions.plotOptions"
|
||||
[dataLabels]="chartOptions.dataLabels">
|
||||
</apx-chart>
|
||||
</div>
|
||||
<div class="card-stats" [ngClass]="hasLanguage ? 'col-3' : 'stats-inline'">
|
||||
<div class="card-stats" [ngClass]="hasLanguage ? 'col-sm-3' : 'stats-inline'">
|
||||
<div class="stat-item" *ngIf="hasLicense">
|
||||
<div class="stat-icon">
|
||||
<fa-icon [icon]="faLicense"></fa-icon>
|
||||
|
||||
@@ -1,18 +1,42 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {Component, Input, OnInit, ViewChild} from '@angular/core';
|
||||
import { faCodeFork, faEye, faStar } from '@fortawesome/free-solid-svg-icons';
|
||||
import {Project} from "../../shared/model/project/project.model";
|
||||
import {Language, Project} from "../../shared/model/project/project.model";
|
||||
import {faScaleBalanced} from "@fortawesome/free-solid-svg-icons/faScaleBalanced";
|
||||
import {
|
||||
ApexAnnotations,
|
||||
ApexChart, ApexDataLabels,
|
||||
ApexNonAxisChartSeries,
|
||||
ApexPlotOptions,
|
||||
ApexResponsive,
|
||||
ChartComponent
|
||||
} from "ng-apexcharts";
|
||||
|
||||
|
||||
export type ChartOptions = {
|
||||
series: ApexNonAxisChartSeries;
|
||||
colors: string[];
|
||||
chart: ApexChart;
|
||||
responsive: ApexResponsive[];
|
||||
labels: string[];
|
||||
plotOptions: ApexPlotOptions;
|
||||
dataLabels: ApexDataLabels;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-card',
|
||||
templateUrl: './project-card.component.html',
|
||||
styleUrls: ['./project-card.component.css']
|
||||
})
|
||||
export class ProjectCardComponent {
|
||||
export class ProjectCardComponent implements OnInit {
|
||||
@Input() inverted: boolean = false;
|
||||
|
||||
@Input() project!: Project;
|
||||
|
||||
@ViewChild('language-chart')
|
||||
languageChart: ChartComponent | undefined;
|
||||
|
||||
chartOptions: ChartOptions | undefined;
|
||||
|
||||
// Stats Icons Definitions
|
||||
faLicense = faScaleBalanced;
|
||||
|
||||
@@ -22,6 +46,13 @@ export class ProjectCardComponent {
|
||||
|
||||
faEye = faEye;
|
||||
|
||||
ngOnInit() {
|
||||
if (!!this.project.languages) {
|
||||
this.chartOptions = this.generateChart(this.project.languages);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
get hasLicense(): boolean {
|
||||
return this.project.license !== undefined;
|
||||
}
|
||||
@@ -30,4 +61,37 @@ export class ProjectCardComponent {
|
||||
return this.project.languages !== undefined &&
|
||||
this.project.languages?.length > 0;
|
||||
}
|
||||
|
||||
private generateChart(languages: Language[]): ChartOptions {
|
||||
return {
|
||||
series: languages.map(value => value.percentage),
|
||||
colors: languages.map(value => value.color),
|
||||
chart: {
|
||||
width: 380,
|
||||
type: "pie"
|
||||
},
|
||||
labels: languages.map(value => value.name),
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 300
|
||||
},
|
||||
legend: {
|
||||
position: "bottom"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
plotOptions: {
|
||||
pie: {
|
||||
expandOnClick: true,
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {CommonModule, NgOptimizedImage} from "@angular/common";
|
||||
import { ProjectCardComponent } from './project-card/project-card.component';
|
||||
import {MatIconModule} from "@angular/material/icon";
|
||||
import {FontAwesomeModule} from "@fortawesome/angular-fontawesome";
|
||||
import {NgApexchartsModule} from "ng-apexcharts";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -14,7 +15,8 @@ import {FontAwesomeModule} from "@fortawesome/angular-fontawesome";
|
||||
CommonModule,
|
||||
NgOptimizedImage,
|
||||
MatIconModule,
|
||||
FontAwesomeModule
|
||||
FontAwesomeModule,
|
||||
NgApexchartsModule
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
|
||||
@@ -15,9 +15,17 @@ export class GithubService {
|
||||
|
||||
GITHUB_API_URL = 'https://api.github.com';
|
||||
|
||||
GITHUB_API_COLORS = 'https://raw.githubusercontent.com/ozh/github-colors/master/colors.json';
|
||||
|
||||
GITHUB_USER = environment.githubUser;
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
colors!: Map<string, string>;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.getLanguageColor().subscribe((colors: Map<string, string>) => {
|
||||
this.colors = colors;
|
||||
});
|
||||
}
|
||||
|
||||
getProjects(): Observable<Project> {
|
||||
return this.http.get(this.apiReposString()).pipe(
|
||||
@@ -61,6 +69,7 @@ export class GithubService {
|
||||
return Object.keys(languages).map((language: string) => {
|
||||
return {
|
||||
name: language,
|
||||
color: this.colors.get(language) || this.getRandColor(),
|
||||
percentage: (languages[language]/totalBytes)*100
|
||||
} as Language;
|
||||
});
|
||||
@@ -68,6 +77,22 @@ export class GithubService {
|
||||
);
|
||||
}
|
||||
|
||||
private getLanguageColor(): Observable<Map<string, string>> {
|
||||
return this.http.get(this.GITHUB_API_COLORS).pipe(
|
||||
map((colors: any) => {
|
||||
const colorMap = new Map<string, string>();
|
||||
Object.keys(colors).forEach((language: string) => {
|
||||
colorMap.set(language, colors[language].color);
|
||||
});
|
||||
return colorMap;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private getRandColor(): string {
|
||||
return `#${Math.floor(Math.random()*16777215).toString(16)}`;
|
||||
}
|
||||
|
||||
private apiReposString() {
|
||||
return `${this.GITHUB_API_URL}/users/${this.GITHUB_USER}/repos`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export type Language = {
|
||||
name: string;
|
||||
color: string;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
// Environment variables
|
||||
window["env"]["BACKEND_URL"] = "${BACKEND_URL}";
|
||||
window["env"]["BACKEND_OAUTH_URL"] = "${BACKEND_OAUTH_URL}";
|
||||
window["env"]["GITHUB_USER"] = "${GITHUB_USER}";
|
||||
})(this);
|
||||
|
||||
Reference in New Issue
Block a user