Added a bunch of stuff
This commit is contained in:
25
src/components/chart/chart-type.ts
Normal file
25
src/components/chart/chart-type.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export enum ChartType {
|
||||
Bar = 'bar',
|
||||
Line = 'line',
|
||||
Scatter = 'scatter',
|
||||
Bubble = 'bubble',
|
||||
Pie = 'pie',
|
||||
Doughnut = 'doughnut',
|
||||
PolarArea = 'polarArea',
|
||||
Radar = 'radar',
|
||||
}
|
||||
|
||||
export interface IDataset {
|
||||
type?: ChartType;
|
||||
borderColor?: string;
|
||||
data: string[];
|
||||
backgroundColor?: string | string[];
|
||||
pointBackgroundColor?: string;
|
||||
pointBorderColor?: string;
|
||||
}
|
||||
|
||||
export interface IData {
|
||||
labels?: string[];
|
||||
labelsSource: string[];
|
||||
datasets: IDataset[];
|
||||
}
|
||||
1
src/components/chart/chart.component.html
Normal file
1
src/components/chart/chart.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<canvas #chart></canvas>
|
||||
0
src/components/chart/chart.component.scss
Normal file
0
src/components/chart/chart.component.scss
Normal file
23
src/components/chart/chart.component.spec.ts
Normal file
23
src/components/chart/chart.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChartComponent } from './chart.component';
|
||||
|
||||
describe('ChartComponent', () => {
|
||||
let component: ChartComponent;
|
||||
let fixture: ComponentFixture<ChartComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ChartComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ChartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
54
src/components/chart/chart.component.ts
Normal file
54
src/components/chart/chart.component.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ElementRef,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Chart, ChartOptions } from 'chart.js';
|
||||
import { PluginNodata } from './plugin-nodata';
|
||||
import { PluginMoreColors } from './plugin-more-colors';
|
||||
import { ChartType, IData } from './chart-type';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-chart',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './chart.component.html',
|
||||
styleUrl: './chart.component.scss',
|
||||
})
|
||||
export class ChartComponent implements AfterViewInit, OnDestroy {
|
||||
@ViewChild('chart') canvas!: ElementRef;
|
||||
|
||||
private chart: any = undefined;
|
||||
private data: IData = {
|
||||
labels: [],
|
||||
labelsSource: [],
|
||||
datasets: [],
|
||||
};
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.initChart();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.chart?.destroy?.();
|
||||
}
|
||||
|
||||
initChart() {
|
||||
this.chart?.destroy?.();
|
||||
|
||||
const opts: ChartOptions = {};
|
||||
opts.responsive = true;
|
||||
opts.maintainAspectRatio = false;
|
||||
|
||||
this.chart = new Chart(this.canvas.nativeElement, {
|
||||
type: ChartType.Bar,
|
||||
data: this.data,
|
||||
options: opts,
|
||||
plugins: [PluginNodata.config(), PluginMoreColors.config()] as any[],
|
||||
});
|
||||
}
|
||||
}
|
||||
73
src/components/chart/plugin-more-colors.ts
Normal file
73
src/components/chart/plugin-more-colors.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ChartType, IDataset } from './chart-type';
|
||||
|
||||
interface IOptions {
|
||||
colors: string[];
|
||||
fills: string[];
|
||||
fillAlpha: number;
|
||||
}
|
||||
|
||||
interface IChart {
|
||||
config: { type: ChartType };
|
||||
ctx: CanvasRenderingContext2D;
|
||||
data: { datasets: IDataset[] };
|
||||
}
|
||||
|
||||
export class PluginMoreColors {
|
||||
static config() {
|
||||
return {
|
||||
id: 'morecolors',
|
||||
beforeUpdate: function (chart: IChart, args: any, options: IOptions) {
|
||||
const { datasets } = chart?.data ?? { datasets: [] };
|
||||
const { colors, fills, fillAlpha } = options;
|
||||
const c_length = colors?.length ?? 0;
|
||||
const f_length = fills?.length ?? 0;
|
||||
|
||||
if (c_length === 0) return;
|
||||
|
||||
datasets.forEach((dataset: IDataset, index: number) => {
|
||||
const color = colors[index % c_length];
|
||||
const fill = f_length > 0 ? fills[index % f_length] : undefined;
|
||||
|
||||
switch (dataset.type ?? chart?.config?.type) {
|
||||
case ChartType.Line:
|
||||
case ChartType.Radar:
|
||||
case ChartType.Scatter:
|
||||
// line connecting points
|
||||
dataset.borderColor = dataset.borderColor ?? color;
|
||||
if (fill)
|
||||
dataset.backgroundColor = dataset.backgroundColor ?? fill;
|
||||
// points
|
||||
dataset.pointBorderColor = dataset.pointBorderColor ?? color;
|
||||
if (fill)
|
||||
dataset.pointBackgroundColor =
|
||||
dataset.pointBackgroundColor ?? fill;
|
||||
break;
|
||||
case ChartType.Doughnut:
|
||||
case ChartType.Pie:
|
||||
case ChartType.PolarArea:
|
||||
const current = dataset.backgroundColor ?? [];
|
||||
dataset.backgroundColor = (dataset.data ?? []).map(
|
||||
(_, index: number) => current[index] ?? colors[index % length]
|
||||
);
|
||||
break;
|
||||
case ChartType.Bar:
|
||||
dataset.borderColor = dataset.borderColor ?? color;
|
||||
if (fill)
|
||||
dataset.backgroundColor = dataset.backgroundColor ?? fill;
|
||||
break;
|
||||
default:
|
||||
dataset.borderColor = dataset.borderColor ?? color;
|
||||
if (fill)
|
||||
dataset.backgroundColor = dataset.backgroundColor ?? fill;
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
defaults: {
|
||||
colors: [],
|
||||
fills: [],
|
||||
fillAlpha: 1.0,
|
||||
} as IOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
75
src/components/chart/plugin-nodata.ts
Normal file
75
src/components/chart/plugin-nodata.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { ChartType, IDataset } from './chart-type';
|
||||
|
||||
interface IOptions {
|
||||
type: ChartType;
|
||||
color: string;
|
||||
innerRadius: number;
|
||||
radius: string | number;
|
||||
}
|
||||
|
||||
interface IArea {
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
}
|
||||
|
||||
interface IChart {
|
||||
chartArea: IArea;
|
||||
config: { type: ChartType };
|
||||
ctx: CanvasRenderingContext2D;
|
||||
data: { datasets: IDataset[] };
|
||||
}
|
||||
|
||||
export class PluginNodata {
|
||||
static config() {
|
||||
const calculate = (radius: number, config: string | number): number => {
|
||||
if (typeof config === 'string' && config.indexOf('%') > -1) {
|
||||
const value = +config.replace('%', '') / 100;
|
||||
return value * radius;
|
||||
} else {
|
||||
return config as number;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
id: 'nodata',
|
||||
afterDraw(chart: IChart, args: any, options: IOptions) {
|
||||
const { datasets } = chart.data ?? { datasets: [] };
|
||||
const { type, color, innerRadius, radius } = options;
|
||||
|
||||
const hasData: boolean = datasets
|
||||
.map((ds: IDataset) => ds.data?.length > 0)
|
||||
.reduce((acc: boolean, cur: boolean) => acc || cur, false);
|
||||
|
||||
if (!hasData) {
|
||||
const {
|
||||
chartArea: { left, top, right, bottom },
|
||||
ctx,
|
||||
} = chart;
|
||||
|
||||
if (type === ChartType.Pie) {
|
||||
const centerX = (left + right) / 2;
|
||||
const centerY = (top + bottom) / 2;
|
||||
const r = Math.min(right - left, bottom - top) / 2;
|
||||
const ir = calculate(r, innerRadius);
|
||||
const or = calculate(r, radius);
|
||||
const width = or - ir;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = width;
|
||||
ctx.strokeStyle = color ?? 'rgba(250, 250, 250, 0.5)';
|
||||
ctx.arc(centerX, centerY, or - width / 2, 0, 2 * Math.PI);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
},
|
||||
defaults: {
|
||||
type: ChartType.Pie,
|
||||
color: 'rgba(250, 250, 250, 0.5)',
|
||||
innerRadius: 0,
|
||||
radius: '100%',
|
||||
} as IOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
4
src/components/select/select.component.html
Normal file
4
src/components/select/select.component.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<label for="select">{{ label }}</label>
|
||||
<select name="select">
|
||||
<option *ngFor="let item of data" [value]="item">{{ item }}</option>
|
||||
</select>
|
||||
0
src/components/select/select.component.scss
Normal file
0
src/components/select/select.component.scss
Normal file
23
src/components/select/select.component.spec.ts
Normal file
23
src/components/select/select.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SelectComponent } from './select.component';
|
||||
|
||||
describe('SelectComponent', () => {
|
||||
let component: SelectComponent;
|
||||
let fixture: ComponentFixture<SelectComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SelectComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SelectComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
17
src/components/select/select.component.ts
Normal file
17
src/components/select/select.component.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Component, Input, inject } from '@angular/core';
|
||||
import { DataService } from '../../services/data.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-select',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './select.component.html',
|
||||
styleUrl: './select.component.scss',
|
||||
})
|
||||
export class SelectComponent {
|
||||
@Input() label!: string;
|
||||
@Input() data!: (string | number)[];
|
||||
|
||||
private dataService = inject(DataService);
|
||||
}
|
||||
Reference in New Issue
Block a user