diff --git a/libs/client-d3-charts/package.json b/libs/client-d3-charts/package.json
index 21f81bdc..c1c235b0 100644
--- a/libs/client-d3-charts/package.json
+++ b/libs/client-d3-charts/package.json
@@ -1,6 +1,6 @@
{
"name": "@rfprodz/client-d3-charts",
- "version": "1.3.16",
+ "version": "1.3.17",
"description": "Angular chart components based on D3JS (https://d3js.org).",
"keywords": [
"angular-charts",
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.html b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.html
new file mode 100644
index 00000000..fea8c58d
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.html
@@ -0,0 +1,3 @@
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.scss b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.scss
new file mode 100644
index 00000000..e477add8
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.scss
@@ -0,0 +1,13 @@
+:host {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ .container {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ width: 100%;
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.spec.ts
new file mode 100644
index 00000000..dd69e40f
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.spec.ts
@@ -0,0 +1,33 @@
+import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { firstValueFrom } from 'rxjs';
+
+import { AppChartExamplesBarComponent } from './chart-examples-bar.component';
+
+describe('AppChartExamplesBarComponent', () => {
+ const testBedConfig: TestModuleMetadata = {
+ declarations: [AppChartExamplesBarComponent],
+ };
+
+ let fixture: ComponentFixture;
+ let component: AppChartExamplesBarComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule(testBedConfig).compileComponents();
+ fixture = TestBed.createComponent(AppChartExamplesBarComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be defined', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('the chart options should have expected structure', async () => {
+ const config = await firstValueFrom(component.barChartConfig$);
+ expect(config.options).toEqual({
+ chartTitle: 'Example bar chart',
+ xAxisTitle: 'long x axis title',
+ yAxisTitle: 'long y axis title',
+ });
+ });
+});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.ts
new file mode 100644
index 00000000..dbdff076
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-bar/chart-examples-bar.component.ts
@@ -0,0 +1,56 @@
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { first, map, switchMap, timer } from 'rxjs';
+
+import { IBarChartOptions, TBarChartData } from '../../interfaces/bar-chart.interface';
+
+/**
+ * BAr chart examples.
+ */
+@Component({
+ selector: 'app-chart-examples-bar',
+ templateUrl: './chart-examples-bar.component.html',
+ styleUrls: ['./chart-examples-bar.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppChartExamplesBarComponent {
+ /**
+ * Sample bar chart data.
+ */
+ private get barChartData() {
+ return [
+ { title: 'one', value: 1 },
+ { title: 'two', value: 2 },
+ { title: 'three', value: 3 },
+ { title: 'four', value: 4 },
+ { title: 'five', value: 5 },
+ ];
+ }
+
+ private readonly breakpoint$ = this.breakpointObserver
+ .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
+ .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
+
+ public readonly barChartConfig$ = this.breakpoint$.pipe(
+ switchMap(() => {
+ const timeout = 100;
+ return timer(timeout).pipe(
+ first(),
+ map(() => ({ data: this.barChartData, options: this.barChartOptions() })),
+ );
+ }),
+ );
+
+ constructor(private readonly breakpointObserver: BreakpointObserver) {}
+
+ /**
+ * Example bar chart options.
+ */
+ private barChartOptions() {
+ return >{
+ chartTitle: 'Example bar chart',
+ xAxisTitle: 'long x axis title',
+ yAxisTitle: 'long y axis title',
+ };
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.html b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.html
new file mode 100644
index 00000000..150eff04
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.html
@@ -0,0 +1,7 @@
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.scss b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.scss
new file mode 100644
index 00000000..e477add8
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.scss
@@ -0,0 +1,13 @@
+:host {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ .container {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ width: 100%;
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.spec.ts
new file mode 100644
index 00000000..1a1d8035
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.spec.ts
@@ -0,0 +1,31 @@
+import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { firstValueFrom } from 'rxjs';
+
+import { AppChartExamplesForceDirectedComponent } from './chart-examples-force-directed.component';
+
+describe('AppChartExamplesForceDirectedComponent', () => {
+ const testBedConfig: TestModuleMetadata = {
+ declarations: [AppChartExamplesForceDirectedComponent],
+ };
+
+ let fixture: ComponentFixture;
+ let component: AppChartExamplesForceDirectedComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule(testBedConfig).compileComponents();
+ fixture = TestBed.createComponent(AppChartExamplesForceDirectedComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be defined', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('the chart options should have expected structure', async () => {
+ const config = await firstValueFrom(component.forceDirectedChartConfig$);
+ expect(config.options).toEqual({
+ chartTitle: 'Example force directed chart',
+ });
+ });
+});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.ts
new file mode 100644
index 00000000..251c9037
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-force-directed/chart-examples-force-directed.component.ts
@@ -0,0 +1,90 @@
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { first, map, switchMap, timer } from 'rxjs';
+
+import {
+ IForceDirectedChartData,
+ IForceDirectedChartOptions,
+ IForceDirectedGraphEntity,
+} from '../../interfaces/force-directed-chart.interface';
+
+@Component({
+ selector: 'app-chart-examples-force-directed',
+ templateUrl: './chart-examples-force-directed.component.html',
+ styleUrls: ['./chart-examples-force-directed.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppChartExamplesForceDirectedComponent {
+ /**
+ * Sample force directed chart data.
+ */
+ private get forceDirectedChartData() {
+ const input = {
+ domains: ['first', 'second', 'third'],
+ entities: [
+ { name: 'one', domains: ['first'], img: '' },
+ { name: 'two', domains: ['second'], img: '' },
+ { name: 'three', domains: ['third'], img: '' },
+ { name: 'four', domains: ['first', 'second'], img: '' },
+ { name: 'five', domains: ['second'], img: '' },
+ { name: 'six', domains: ['third', 'second'], img: '' },
+ { name: 'seven', domains: ['second'], img: '' },
+ { name: 'eight', domains: ['third'], img: '' },
+ ],
+ };
+ const domains: IForceDirectedChartData['domains'] = input.domains.map((name, index) => ({ index, name, value: 1 }));
+ const entities: IForceDirectedChartData['entities'] = input.entities.map((app, index) => ({
+ index: index,
+ name: app.name,
+ domains: [...app.domains],
+ img: app.img,
+ linksCount: app.domains.length,
+ }));
+ const nodes: IForceDirectedGraphEntity[] = [...entities];
+ const links: IForceDirectedChartData['links'] = entities
+ .map(entity => {
+ return entity.domains.map(domain => {
+ const source = domains.find(value => domain === value.name)?.index ?? -1;
+ const target = entity.index;
+ return { source, target };
+ });
+ })
+ .reduce((accumulator, item) => (Array.isArray(item) ? [...accumulator, ...item] : [...accumulator, item]), [])
+ .filter(link => link.source !== -1 && link.target !== -1 && typeof link.target !== 'undefined');
+ const chartData: IForceDirectedChartData = {
+ domains,
+ entities: entities.map(item => ({
+ ...item,
+ linksCount: links.reduce((acc, link) => (link.target === item.index ? acc + 1 : acc), 0),
+ })),
+ links,
+ nodes,
+ };
+ return chartData;
+ }
+
+ private readonly breakpoint$ = this.breakpointObserver
+ .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
+ .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
+
+ public readonly forceDirectedChartConfig$ = this.breakpoint$.pipe(
+ switchMap(() => {
+ const timeout = 100;
+ return timer(timeout).pipe(
+ first(),
+ map(() => ({ data: this.forceDirectedChartData, options: this.forceDirectedChartOptions() })),
+ );
+ }),
+ );
+
+ constructor(private readonly breakpointObserver: BreakpointObserver) {}
+
+ /**
+ * Example force directed chart options.
+ */
+ private forceDirectedChartOptions() {
+ return >{
+ chartTitle: 'Example force directed chart',
+ };
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.html b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.html
new file mode 100644
index 00000000..a99fc1e5
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.html
@@ -0,0 +1,13 @@
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.scss b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.scss
new file mode 100644
index 00000000..e477add8
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.scss
@@ -0,0 +1,13 @@
+:host {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ .container {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ width: 100%;
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.spec.ts
new file mode 100644
index 00000000..a2a7196c
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.spec.ts
@@ -0,0 +1,34 @@
+import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { firstValueFrom } from 'rxjs';
+
+import { AppChartExamplesLineComponent } from './chart-examples-line.component';
+
+describe('AppChartExamplesLineComponent', () => {
+ const testBedConfig: TestModuleMetadata = {
+ declarations: [AppChartExamplesLineComponent],
+ };
+
+ let fixture: ComponentFixture;
+ let component: AppChartExamplesLineComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule(testBedConfig).compileComponents();
+ fixture = TestBed.createComponent(AppChartExamplesLineComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be defined', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('the chart options should have expected structure', async () => {
+ const config = await firstValueFrom(component.lineChartConfig$);
+ expect(config.options).toEqual({
+ chartTitle: 'Example line chart, date format default',
+ dateFormat: 'default',
+ xAxisTitle: 'Date range',
+ yAxisTitle: 'Value range',
+ });
+ });
+});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.ts
new file mode 100644
index 00000000..0c007fbd
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-line/chart-examples-line.component.ts
@@ -0,0 +1,96 @@
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { first, map, switchMap, timer } from 'rxjs';
+
+import { ILineChartOptions, TDateFormat, TLineChartData } from '../../interfaces/line-chart.interface';
+
+/**
+ * Line chart examples.
+ */
+@Component({
+ selector: 'app-chart-examples-line',
+ templateUrl: './chart-examples-line.component.html',
+ styleUrls: ['./chart-examples-line.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppChartExamplesLineComponent {
+ /**
+ * Sample line chart data.
+ */
+ private get lineChartData() {
+ return [
+ [
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ ].sort((a, b) => a.timestamp - b.timestamp),
+ [
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ ].sort((a, b) => a.timestamp - b.timestamp),
+ [
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ { timestamp: this.randomTimestamp(), value: this.randomValue() },
+ ].sort((a, b) => a.timestamp - b.timestamp),
+ ];
+ }
+
+ private readonly breakpoint$ = this.breakpointObserver
+ .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
+ .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
+
+ public readonly lineChartConfig$ = this.breakpoint$.pipe(
+ switchMap(() => {
+ const timeout = 100;
+ return timer(timeout).pipe(
+ first(),
+ map(() => ({
+ data: this.lineChartData,
+ options: this.lineChartOptions(),
+ optionsDateDdMmYy: this.lineChartOptions('dd/mm/yy'),
+ optionsDateDdMmYyyy: this.lineChartOptions('dd/mm/yyyy'),
+ optionsDateMmYyyy: this.lineChartOptions('mm/yyyy'),
+ })),
+ );
+ }),
+ );
+
+ constructor(private readonly breakpointObserver: BreakpointObserver) {}
+
+ private randomValue(range?: number) {
+ const defaultRange = 100;
+ return Math.floor(Math.random() * (range ?? defaultRange) + 1);
+ }
+
+ private randomTimestamp(range?: number) {
+ const defaultRange = 100000000;
+ return Math.floor(Math.random() * (range ?? defaultRange) + new Date().getTime());
+ }
+
+ /**
+ * Example line chart options.
+ * @param dateFormat date format
+ */
+ private lineChartOptions(dateFormat: TDateFormat = 'default') {
+ return >{
+ chartTitle: `Example line chart, date format ${dateFormat}`,
+ xAxisTitle: 'Date range',
+ yAxisTitle: 'Value range',
+ dateFormat,
+ };
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.html b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.html
new file mode 100644
index 00000000..97ab5ce4
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.html
@@ -0,0 +1,3 @@
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.scss b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.scss
new file mode 100644
index 00000000..e477add8
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.scss
@@ -0,0 +1,13 @@
+:host {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ .container {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ width: 100%;
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.spec.ts
new file mode 100644
index 00000000..c7996a1f
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.spec.ts
@@ -0,0 +1,31 @@
+import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { firstValueFrom } from 'rxjs';
+
+import { AppChartExamplesPieComponent } from './chart-examples-pie.component';
+
+describe('AppChartExamplesPieComponent', () => {
+ const testBedConfig: TestModuleMetadata = {
+ declarations: [AppChartExamplesPieComponent],
+ };
+
+ let fixture: ComponentFixture;
+ let component: AppChartExamplesPieComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule(testBedConfig).compileComponents();
+ fixture = TestBed.createComponent(AppChartExamplesPieComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be defined', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('the chart options should have expected structure', async () => {
+ const config = await firstValueFrom(component.pieChartConfig$);
+ expect(config.options).toEqual({
+ chartTitle: 'Example pie chart',
+ });
+ });
+});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.ts
new file mode 100644
index 00000000..f5bfc4d7
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-pie/chart-examples-pie.component.ts
@@ -0,0 +1,55 @@
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { first, map, switchMap, timer } from 'rxjs';
+
+import { IPieChartDataNode, IPieChartOptions } from '../../interfaces/pie-chart.interface';
+
+/**
+ * Pie chart examples.
+ */
+@Component({
+ selector: 'app-chart-examples-pie',
+ templateUrl: './chart-examples-pie.component.html',
+ styleUrls: ['./chart-examples-pie.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppChartExamplesPieComponent {
+ /**
+ * Sample pie chart data.
+ */
+ private get pieChartData() {
+ return [
+ { key: 'one', y: 1 },
+ { key: 'two', y: 2 },
+ { key: 'three', y: 3 },
+ { key: 'four', y: 4 },
+ { key: 'five', y: 5 },
+ { key: 'six', y: 6 },
+ ];
+ }
+
+ private readonly breakpoint$ = this.breakpointObserver
+ .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
+ .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
+
+ public readonly pieChartConfig$ = this.breakpoint$.pipe(
+ switchMap(() => {
+ const timeout = 100;
+ return timer(timeout).pipe(
+ first(),
+ map(() => ({ data: this.pieChartData, options: this.pieChartOptions() })),
+ );
+ }),
+ );
+
+ constructor(private readonly breakpointObserver: BreakpointObserver) {}
+
+ /**
+ * Example pie chart options.
+ */
+ private pieChartOptions() {
+ return >{
+ chartTitle: 'Example pie chart',
+ };
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.html b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.html
new file mode 100644
index 00000000..a3e35709
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.html
@@ -0,0 +1,7 @@
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.scss b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.scss
new file mode 100644
index 00000000..e477add8
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.scss
@@ -0,0 +1,13 @@
+:host {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: center;
+
+ .container {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ width: 100%;
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.spec.ts
new file mode 100644
index 00000000..28b3ff9b
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.spec.ts
@@ -0,0 +1,31 @@
+import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
+import { firstValueFrom } from 'rxjs';
+
+import { AppChartExamplesRadaraComponent } from './chart-examples-radar.component';
+
+describe('AppChartExamplesRadaraComponent', () => {
+ const testBedConfig: TestModuleMetadata = {
+ declarations: [AppChartExamplesRadaraComponent],
+ };
+
+ let fixture: ComponentFixture;
+ let component: AppChartExamplesRadaraComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule(testBedConfig).compileComponents();
+ fixture = TestBed.createComponent(AppChartExamplesRadaraComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be defined', () => {
+ expect(component).toBeDefined();
+ });
+
+ it('the chart options should have expected structure', async () => {
+ const config = await firstValueFrom(component.radarChartConfig$);
+ expect(config.options).toEqual({
+ chartTitle: 'Example radar chart',
+ });
+ });
+});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.ts
new file mode 100644
index 00000000..98c84d24
--- /dev/null
+++ b/libs/client-d3-charts/src/lib/components/chart-examples-radar/chart-examples-radar.component.ts
@@ -0,0 +1,71 @@
+import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { first, map, switchMap, timer } from 'rxjs';
+
+import { IRadarChartDataNode, IRadarChartOptions } from '../../interfaces/radar-chart.interface';
+
+/**
+ * Radar chart examples.
+ */
+@Component({
+ selector: 'app-chart-examples-radar',
+ templateUrl: './chart-examples-radar.component.html',
+ styleUrls: ['./chart-examples-radar.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class AppChartExamplesRadaraComponent {
+ /**
+ * Sample radar chart data.
+ */
+ private get radarChartData() {
+ return [
+ [
+ { axis: 'one', value: 1, unit: 'x' },
+ { axis: 'two', value: 2, unit: 'x' },
+ { axis: 'three', value: 3, unit: 'x' },
+ { axis: 'four', value: 4, unit: 'x' },
+ { axis: 'five', value: 5, unit: 'x' },
+ { axis: 'six', value: 6, unit: 'x' },
+ { axis: 'seven', value: 7, unit: 'x' },
+ { axis: 'eight', value: 8, unit: 'x' },
+ { axis: 'nine (long labels are wrapped)', value: 9, unit: 'x' },
+ ],
+ [
+ { axis: 'one', value: 9, unit: 'y' },
+ { axis: 'two', value: 8, unit: 'y' },
+ { axis: 'three', value: 7, unit: 'y' },
+ { axis: 'four', value: 6, unit: 'y' },
+ { axis: 'five', value: 5, unit: 'y' },
+ { axis: 'six', value: 4, unit: 'y' },
+ { axis: 'seven', value: 3, unit: 'y' },
+ { axis: 'eight', value: 2, unit: 'y' },
+ { axis: 'nine (long labels are wrapped)', value: 1, unit: 'y' },
+ ],
+ ];
+ }
+
+ private readonly breakpoint$ = this.breakpointObserver
+ .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
+ .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
+
+ public readonly radarChartConfig$ = this.breakpoint$.pipe(
+ switchMap(() => {
+ const timeout = 100;
+ return timer(timeout).pipe(
+ first(),
+ map(() => ({ data: this.radarChartData, options: this.radarChartOptions() })),
+ );
+ }),
+ );
+
+ constructor(private readonly breakpointObserver: BreakpointObserver) {}
+
+ /**
+ * Example radar chart options.
+ */
+ private radarChartOptions() {
+ return >{
+ chartTitle: 'Example radar chart',
+ };
+ }
+}
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.html b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.html
index f510de00..aa115692 100644
--- a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.html
+++ b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.html
@@ -1,45 +1,17 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.spec.ts b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.spec.ts
index 2453b10c..81af31e2 100644
--- a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.spec.ts
+++ b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.spec.ts
@@ -1,10 +1,12 @@
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';
import { AppChartExamplesComponent } from './chart-examples.component';
-describe('AppGlobalProgressBarComponent', () => {
+describe('AppChartExamplesComponent', () => {
const testBedConfig: TestModuleMetadata = {
declarations: [AppChartExamplesComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
};
let fixture: ComponentFixture;
@@ -20,39 +22,4 @@ describe('AppGlobalProgressBarComponent', () => {
it('should be defined', () => {
expect(component).toBeDefined();
});
-
- it('barChartOptions should have expected structure', () => {
- expect(component.barChartOptions()).toEqual({
- chartTitle: 'Example bar chart',
- xAxisTitle: 'long x axis title',
- yAxisTitle: 'long y axis title',
- });
- });
-
- it('lineChartOptions should have expected structure', () => {
- expect(component.lineChartOptions()).toEqual({
- chartTitle: 'Example line chart, date format default',
- dateFormat: 'default',
- xAxisTitle: 'Date range',
- yAxisTitle: 'Value range',
- });
- });
-
- it('radarChartOptions should have expected structure', () => {
- expect(component.radarChartOptions()).toEqual({
- chartTitle: 'Example radar chart',
- });
- });
-
- it('pieChartOptions should have expected structure', () => {
- expect(component.pieChartOptions()).toEqual({
- chartTitle: 'Example pie chart',
- });
- });
-
- it('forceDirectedChartOptions should have expected structure', () => {
- expect(component.forceDirectedChartOptions()).toEqual({
- chartTitle: 'Example force directed chart',
- });
- });
});
diff --git a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.ts b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.ts
index bc199312..e4a92651 100644
--- a/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.ts
+++ b/libs/client-d3-charts/src/lib/components/chart-examples/chart-examples.component.ts
@@ -1,16 +1,4 @@
-import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { ChangeDetectionStrategy, Component } from '@angular/core';
-import { first, map, switchMap, timer } from 'rxjs';
-
-import { IBarChartOptions, TBarChartData } from '../../interfaces/bar-chart.interface';
-import {
- IForceDirectedChartData,
- IForceDirectedChartOptions,
- IForceDirectedGraphEntity,
-} from '../../interfaces/force-directed-chart.interface';
-import { ILineChartOptions, TDateFormat, TLineChartData } from '../../interfaces/line-chart.interface';
-import { IPieChartDataNode, IPieChartOptions } from '../../interfaces/pie-chart.interface';
-import { IRadarChartDataNode, IRadarChartOptions } from '../../interfaces/radar-chart.interface';
@Component({
selector: 'app-chart-examples',
@@ -18,264 +6,4 @@ import { IRadarChartDataNode, IRadarChartOptions } from '../../interfaces/radar-
styleUrls: ['./chart-examples.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class AppChartExamplesComponent {
- /**
- * Sample bar chart data.
- */
- public get barChartData() {
- return [
- { title: 'one', value: 1 },
- { title: 'two', value: 2 },
- { title: 'three', value: 3 },
- { title: 'four', value: 4 },
- { title: 'five', value: 5 },
- ];
- }
-
- /**
- * Sample line chart data.
- */
- public get lineChartData() {
- return [
- [
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- ].sort((a, b) => a.timestamp - b.timestamp),
- [
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- ].sort((a, b) => a.timestamp - b.timestamp),
- [
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- { timestamp: this.randomTimestamp(), value: this.randomValue() },
- ].sort((a, b) => a.timestamp - b.timestamp),
- ];
- }
-
- /**
- * Sample radar chart data.
- */
- public get radarChartData() {
- return [
- [
- { axis: 'one', value: 1, unit: 'x' },
- { axis: 'two', value: 2, unit: 'x' },
- { axis: 'three', value: 3, unit: 'x' },
- { axis: 'four', value: 4, unit: 'x' },
- { axis: 'five', value: 5, unit: 'x' },
- { axis: 'six', value: 6, unit: 'x' },
- { axis: 'seven', value: 7, unit: 'x' },
- { axis: 'eight', value: 8, unit: 'x' },
- { axis: 'nine (long labels are wrapped)', value: 9, unit: 'x' },
- ],
- [
- { axis: 'one', value: 9, unit: 'y' },
- { axis: 'two', value: 8, unit: 'y' },
- { axis: 'three', value: 7, unit: 'y' },
- { axis: 'four', value: 6, unit: 'y' },
- { axis: 'five', value: 5, unit: 'y' },
- { axis: 'six', value: 4, unit: 'y' },
- { axis: 'seven', value: 3, unit: 'y' },
- { axis: 'eight', value: 2, unit: 'y' },
- { axis: 'nine (long labels are wrapped)', value: 1, unit: 'y' },
- ],
- ];
- }
-
- /**
- * Sample pie chart data.
- */
- public get pieChartData() {
- return [
- { key: 'one', y: 1 },
- { key: 'two', y: 2 },
- { key: 'three', y: 3 },
- { key: 'four', y: 4 },
- { key: 'five', y: 5 },
- { key: 'six', y: 6 },
- ];
- }
-
- /**
- * Sample force directed chart data.
- */
- public get forceDirectedChartData() {
- const input = {
- domains: ['first', 'second', 'third'],
- entities: [
- { name: 'one', domains: ['first'], img: '' },
- { name: 'two', domains: ['second'], img: '' },
- { name: 'three', domains: ['third'], img: '' },
- { name: 'four', domains: ['first', 'second'], img: '' },
- { name: 'five', domains: ['second'], img: '' },
- { name: 'six', domains: ['third', 'second'], img: '' },
- { name: 'seven', domains: ['second'], img: '' },
- { name: 'eight', domains: ['third'], img: '' },
- ],
- };
- const domains: IForceDirectedChartData['domains'] = input.domains.map((name, index) => ({ index, name, value: 1 }));
- const entities: IForceDirectedChartData['entities'] = input.entities.map((app, index) => ({
- index: index,
- name: app.name,
- domains: [...app.domains],
- img: app.img,
- linksCount: 0,
- }));
- const nodes: IForceDirectedGraphEntity[] = [...entities];
- const links: IForceDirectedChartData['links'] = entities
- .map(entity => {
- return entity.domains.map(domain => {
- const source = domains.find(value => domain === value.name)?.index ?? -1;
- const target = entity.index;
- return { source, target };
- });
- })
- .reduce((accumulator, item) => (Array.isArray(item) ? [...accumulator, ...item] : [...accumulator, item]), [])
- .filter(link => link.source !== -1 && link.target !== -1 && typeof link.target !== 'undefined');
- const chartData: IForceDirectedChartData = {
- domains,
- entities: entities.map(item => ({
- ...item,
- linksCount: links.reduce((acc, link) => (link.target === item.index ? acc + 1 : acc), 0),
- })),
- links,
- nodes,
- };
- return chartData;
- }
-
- private readonly breakpoint$ = this.breakpointObserver
- .observe([Breakpoints.XSmall, Breakpoints.Small, Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge])
- .pipe(map(result => Object.keys(result.breakpoints).find(item => result.breakpoints[item]) ?? 'unknown'));
-
- public readonly barChartConfig$ = this.breakpoint$.pipe(
- switchMap(() =>
- timer(this.timeout).pipe(
- first(),
- map(() => ({ data: this.barChartData, options: this.barChartOptions() })),
- ),
- ),
- );
-
- public readonly lineChartConfig$ = this.breakpoint$.pipe(
- switchMap(() =>
- timer(this.timeout).pipe(
- first(),
- map(() => ({
- data: this.lineChartData,
- options: this.lineChartOptions(),
- optionsDateDdMmYy: this.lineChartOptions('dd/mm/yy'),
- optionsDateDdMmYyyy: this.lineChartOptions('dd/mm/yyyy'),
- optionsDateMmYyyy: this.lineChartOptions('mm/yyyy'),
- })),
- ),
- ),
- );
-
- public readonly radarChartConfig$ = this.breakpoint$.pipe(
- switchMap(() =>
- timer(this.timeout).pipe(
- first(),
- map(() => ({ data: this.radarChartData, options: this.radarChartOptions() })),
- ),
- ),
- );
-
- public readonly pieChartConfig$ = this.breakpoint$.pipe(
- switchMap(() =>
- timer(this.timeout).pipe(
- first(),
- map(() => ({ data: this.pieChartData, options: this.pieChartOptions() })),
- ),
- ),
- );
-
- public readonly forceDirectedChartConfig$ = this.breakpoint$.pipe(
- switchMap(() =>
- timer(this.timeout).pipe(
- first(),
- map(() => ({ data: this.forceDirectedChartData, options: this.forceDirectedChartOptions() })),
- ),
- ),
- );
-
- constructor(private readonly breakpointObserver: BreakpointObserver) {}
-
- private readonly timeout = 100;
-
- public randomValue(range?: number) {
- const defaultRange = 100;
- return Math.floor(Math.random() * (range ?? defaultRange) + 1);
- }
-
- public randomTimestamp(range?: number) {
- const defaultRange = 100000000;
- return Math.floor(Math.random() * (range ?? defaultRange) + new Date().getTime());
- }
-
- /**
- * Example bar chart options.
- */
- public barChartOptions() {
- return >{
- chartTitle: 'Example bar chart',
- xAxisTitle: 'long x axis title',
- yAxisTitle: 'long y axis title',
- };
- }
-
- /**
- * Example line chart options.
- * @param dateFormat date format
- */
- public lineChartOptions(dateFormat: TDateFormat = 'default') {
- return >{
- chartTitle: `Example line chart, date format ${dateFormat}`,
- xAxisTitle: 'Date range',
- yAxisTitle: 'Value range',
- dateFormat,
- };
- }
-
- /**
- * Example radar chart options.
- */
- public radarChartOptions() {
- return >{
- chartTitle: 'Example radar chart',
- };
- }
-
- /**
- * Example pie chart options.
- */
- public pieChartOptions() {
- return >{
- chartTitle: 'Example pie chart',
- };
- }
-
- /**
- * Example force directed chart options.
- */
- public forceDirectedChartOptions() {
- return >{
- chartTitle: 'Example force directed chart',
- };
- }
-}
+export class AppChartExamplesComponent {}
diff --git a/libs/client-d3-charts/src/lib/d3-charts.module.ts b/libs/client-d3-charts/src/lib/d3-charts.module.ts
index 7b0dfc1d..61b786fe 100644
--- a/libs/client-d3-charts/src/lib/d3-charts.module.ts
+++ b/libs/client-d3-charts/src/lib/d3-charts.module.ts
@@ -3,6 +3,11 @@ import { NgModule } from '@angular/core';
import { AppBarChartComponent } from './components/bar-chart/bar-chart.component';
import { AppChartExamplesComponent } from './components/chart-examples/chart-examples.component';
+import { AppChartExamplesBarComponent } from './components/chart-examples-bar/chart-examples-bar.component';
+import { AppChartExamplesForceDirectedComponent } from './components/chart-examples-force-directed/chart-examples-force-directed.component';
+import { AppChartExamplesLineComponent } from './components/chart-examples-line/chart-examples-line.component';
+import { AppChartExamplesPieComponent } from './components/chart-examples-pie/chart-examples-pie.component';
+import { AppChartExamplesRadaraComponent } from './components/chart-examples-radar/chart-examples-radar.component';
import { AppForceDirectedChartComponent } from './components/force-directed-chart/force-directed-chart.component';
import { AppLineChartComponent } from './components/line-chart/line-chart.component';
import { AppPieChartComponent } from './components/pie-chart/pie-chart.component';
@@ -17,6 +22,11 @@ import { AppRadarChartComponent } from './components/radar-chart/radar-chart.com
AppBarChartComponent,
AppLineChartComponent,
AppChartExamplesComponent,
+ AppChartExamplesLineComponent,
+ AppChartExamplesRadaraComponent,
+ AppChartExamplesBarComponent,
+ AppChartExamplesPieComponent,
+ AppChartExamplesForceDirectedComponent,
],
exports: [
AppPieChartComponent,
diff --git a/libs/client-d3-charts/src/lib/util/force-directed-chart.util.ts b/libs/client-d3-charts/src/lib/util/force-directed-chart.util.ts
index a8a9b271..42016df1 100644
--- a/libs/client-d3-charts/src/lib/util/force-directed-chart.util.ts
+++ b/libs/client-d3-charts/src/lib/util/force-directed-chart.util.ts
@@ -245,20 +245,15 @@ const createNodes = (
force: d3.Simulation,
config: IForceDirectedChartOptions,
) => {
+ const base = 5;
return svg
.selectAll('.node')
.data(data.nodes)
.enter()
.append('circle')
.attr('class', 'node')
- .attr('r', val => {
- const base = 5;
- return base + (val.value ?? 0) + (val.linksCount ?? 0) * 2;
- })
- .style('stroke-width', val => {
- const base = 5;
- return base + (val.value ?? 0) + (val.linksCount ?? 0) * 2;
- })
+ .attr('r', val => base + (val.value ?? 1) * (val.linksCount ?? 1))
+ .style('stroke-width', val => base + (val.value ?? 1) + (val.linksCount ?? 1))
.style('fill', val => (typeof val.img === 'undefined' || val.img === '' ? '#f00000' : `url(${val.img})`))
.call(
d3