Initial commit: data-viz-elements-demo application
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
.angular/
|
||||||
|
*.tgz
|
||||||
76
angular.json
Normal file
76
angular.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"data-viz-elements-demo": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss",
|
||||||
|
"standalone": true,
|
||||||
|
"changeDetection": "OnPush"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:application",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/data-viz-elements-demo",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"browser": "src/main.ts",
|
||||||
|
"polyfills": [
|
||||||
|
"zone.js"
|
||||||
|
],
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [],
|
||||||
|
"preserveSymlinks": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "1MB",
|
||||||
|
"maximumError": "2MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "10kB",
|
||||||
|
"maximumError": "20kB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"optimization": false,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"buildTarget": "data-viz-elements-demo:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildTarget": "data-viz-elements-demo:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14121
package-lock.json
generated
Normal file
14121
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
45
package.json
Normal file
45
package.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "data-viz-elements-demo",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Demo application for data-viz-elements-ui library",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "ng serve",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"serve": "ng serve --open"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "^19.2.0",
|
||||||
|
"@angular/common": "^19.2.0",
|
||||||
|
"@angular/compiler": "^19.2.0",
|
||||||
|
"@angular/core": "^19.2.0",
|
||||||
|
"@angular/forms": "^19.2.0",
|
||||||
|
"@angular/platform-browser": "^19.2.0",
|
||||||
|
"@angular/platform-browser-dynamic": "^19.2.0",
|
||||||
|
"@angular/router": "^19.2.0",
|
||||||
|
"data-viz-elements-ui": "file:../data-viz-elements-ui/dist",
|
||||||
|
"base-ui": "file:../sda-frontend/libs/base-ui/dist",
|
||||||
|
"d3-array": "^3.2.0",
|
||||||
|
"d3-axis": "^3.0.0",
|
||||||
|
"d3-color": "^3.1.0",
|
||||||
|
"d3-format": "^3.1.0",
|
||||||
|
"d3-hierarchy": "^3.1.0",
|
||||||
|
"d3-interpolate": "^3.0.0",
|
||||||
|
"d3-scale": "^4.0.0",
|
||||||
|
"d3-selection": "^3.0.0",
|
||||||
|
"d3-shape": "^3.2.0",
|
||||||
|
"d3-time-format": "^4.1.0",
|
||||||
|
"d3-transition": "^3.0.0",
|
||||||
|
"rxjs": "~7.8.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"zone.js": "~0.15.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "^19.2.0",
|
||||||
|
"@angular/cli": "^19.2.0",
|
||||||
|
"@angular/compiler-cli": "^19.2.0",
|
||||||
|
"typescript": "~5.7.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
221
src/app/app.component.html
Normal file
221
src/app/app.component.html
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
<div class="demo-app">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="demo-header">
|
||||||
|
<h1 class="demo-header__title">Data Viz Elements</h1>
|
||||||
|
<p class="demo-header__subtitle">Angular components for data visualization powered by D3.js</p>
|
||||||
|
<div class="demo-header__actions">
|
||||||
|
<button class="demo-btn demo-btn--ghost" (click)="toggleDarkMode()" type="button">
|
||||||
|
{{ isDarkMode() ? 'Light Mode' : 'Dark Mode' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Navigation -->
|
||||||
|
<nav class="demo-nav">
|
||||||
|
@for (section of sections; track section.id) {
|
||||||
|
<button
|
||||||
|
class="demo-nav__item"
|
||||||
|
[class.demo-nav__item--active]="activeSection() === section.id"
|
||||||
|
(click)="setSection(section.id)"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{{ section.label }}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<main class="demo-content">
|
||||||
|
|
||||||
|
<!-- Standard Charts -->
|
||||||
|
@if (activeSection() === 'standard') {
|
||||||
|
<div class="demo-grid">
|
||||||
|
<section class="demo-card demo-card--wide">
|
||||||
|
<h2 class="demo-card__title">Bar Chart</h2>
|
||||||
|
<viz-bar-chart [data]="barData" [height]="280" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--wide">
|
||||||
|
<h2 class="demo-card__title">Bar Chart (Horizontal)</h2>
|
||||||
|
<viz-bar-chart [data]="barData" [height]="280" orientation="horizontal" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Line Chart</h2>
|
||||||
|
<viz-line-chart [series]="lineSeries" [height]="300" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Area Chart</h2>
|
||||||
|
<viz-area-chart [series]="lineSeries" [height]="300" [gradient]="true" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card">
|
||||||
|
<h2 class="demo-card__title">Pie Chart</h2>
|
||||||
|
<viz-pie-chart [data]="pieData" [height]="280" [legend]="{ visible: true, position: 'right' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card">
|
||||||
|
<h2 class="demo-card__title">Donut Chart</h2>
|
||||||
|
<viz-pie-chart [data]="pieData" [height]="280" [donut]="true" [legend]="{ visible: true, position: 'bottom' }" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Scatter / Bubble Chart</h2>
|
||||||
|
<viz-scatter-chart [series]="scatterSeries" [height]="350" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Statistical Charts -->
|
||||||
|
@if (activeSection() === 'statistical') {
|
||||||
|
<div class="demo-grid">
|
||||||
|
<section class="demo-card demo-card--wide">
|
||||||
|
<h2 class="demo-card__title">Histogram</h2>
|
||||||
|
<viz-histogram [values]="histogramValues" [height]="300" [bins]="25" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--wide">
|
||||||
|
<h2 class="demo-card__title">Box Plot</h2>
|
||||||
|
<viz-box-plot [data]="boxPlotData" [height]="300" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Heatmap</h2>
|
||||||
|
<viz-heatmap
|
||||||
|
[data]="heatmapData"
|
||||||
|
[xLabels]="heatmapXLabels"
|
||||||
|
[yLabels]="heatmapYLabels"
|
||||||
|
[height]="300"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Treemap</h2>
|
||||||
|
<viz-treemap [data]="treemapData" [height]="350" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Real-time & KPI -->
|
||||||
|
@if (activeSection() === 'realtime') {
|
||||||
|
<div class="demo-grid">
|
||||||
|
<!-- Stat Cards -->
|
||||||
|
<section class="demo-card">
|
||||||
|
<viz-stat-card
|
||||||
|
value="12,847"
|
||||||
|
label="Total Users"
|
||||||
|
trend="up"
|
||||||
|
trendValue="+12.5%"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section class="demo-card">
|
||||||
|
<viz-stat-card
|
||||||
|
value="$48.2K"
|
||||||
|
label="Revenue"
|
||||||
|
trend="up"
|
||||||
|
trendValue="+8.3%"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section class="demo-card">
|
||||||
|
<viz-stat-card
|
||||||
|
value="342"
|
||||||
|
label="Active Sessions"
|
||||||
|
trend="down"
|
||||||
|
trendValue="-3.1%"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section class="demo-card">
|
||||||
|
<viz-stat-card
|
||||||
|
value="99.9%"
|
||||||
|
label="Uptime"
|
||||||
|
trend="flat"
|
||||||
|
trendValue="0.0%"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Gauges -->
|
||||||
|
<section class="demo-card demo-card--center">
|
||||||
|
<h2 class="demo-card__title">Radial Gauge</h2>
|
||||||
|
<viz-gauge [value]="72" [max]="100" label="CPU" [width]="180" [height]="140" />
|
||||||
|
</section>
|
||||||
|
<section class="demo-card demo-card--center">
|
||||||
|
<h2 class="demo-card__title">Radial Gauge</h2>
|
||||||
|
<viz-gauge [value]="45" [max]="100" label="Memory" [width]="180" [height]="140" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Progress Rings -->
|
||||||
|
<section class="demo-card demo-card--center">
|
||||||
|
<h2 class="demo-card__title">Progress Rings</h2>
|
||||||
|
<div class="demo-row">
|
||||||
|
<viz-progress-ring [value]="75" [max]="100" [size]="80" />
|
||||||
|
<viz-progress-ring [value]="42" [max]="100" [size]="80" color="#10b981" />
|
||||||
|
<viz-progress-ring [value]="90" [max]="100" [size]="80" color="#f59e0b" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Progress Bars -->
|
||||||
|
<section class="demo-card demo-card--wide">
|
||||||
|
<h2 class="demo-card__title">Progress Bar</h2>
|
||||||
|
<div class="demo-stack">
|
||||||
|
<viz-progress-bar [value]="68" [max]="100" />
|
||||||
|
<viz-progress-bar [value]="45" [max]="100" color="#10b981" />
|
||||||
|
<viz-progress-bar [value]="82" [max]="100" color="#f59e0b" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Trend Indicators -->
|
||||||
|
<section class="demo-card">
|
||||||
|
<h2 class="demo-card__title">Trend Indicators</h2>
|
||||||
|
<div class="demo-stack">
|
||||||
|
<viz-trend-indicator direction="up" value="+15.3%" label="vs last month" />
|
||||||
|
<viz-trend-indicator direction="down" value="-4.2%" label="vs last week" />
|
||||||
|
<viz-trend-indicator direction="flat" value="0.0%" label="vs yesterday" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Sparklines -->
|
||||||
|
<section class="demo-card">
|
||||||
|
<h2 class="demo-card__title">Sparklines</h2>
|
||||||
|
<div class="demo-stack">
|
||||||
|
<div class="demo-row demo-row--between">
|
||||||
|
<span>Line:</span>
|
||||||
|
<viz-sparkline [data]="sparklineData" type="line" [showLastValue]="true" />
|
||||||
|
</div>
|
||||||
|
<div class="demo-row demo-row--between">
|
||||||
|
<span>Area:</span>
|
||||||
|
<viz-sparkline [data]="sparklineData" type="area" color="#10b981" />
|
||||||
|
</div>
|
||||||
|
<div class="demo-row demo-row--between">
|
||||||
|
<span>Bar:</span>
|
||||||
|
<viz-sparkline [data]="sparklineData" type="bar" color="#f59e0b" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Time Series -->
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Time Series</h2>
|
||||||
|
<viz-time-series [series]="timeSeriesData" [height]="300" [showArea]="true" [legend]="{ visible: true, position: 'top' }" />
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- Data Table -->
|
||||||
|
@if (activeSection() === 'table') {
|
||||||
|
<div class="demo-grid">
|
||||||
|
<section class="demo-card demo-card--full">
|
||||||
|
<h2 class="demo-card__title">Data Table</h2>
|
||||||
|
<viz-data-table
|
||||||
|
[data]="tableData"
|
||||||
|
[columns]="tableColumns"
|
||||||
|
[sortable]="true"
|
||||||
|
[filterable]="true"
|
||||||
|
[paginated]="true"
|
||||||
|
[pageSize]="8"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
142
src/app/app.component.scss
Normal file
142
src/app/app.component.scss
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
.demo-app {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header
|
||||||
|
.demo-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button
|
||||||
|
.demo-btn {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--ghost {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
.demo-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
padding-bottom: 0;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-bottom-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid
|
||||||
|
.demo-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card
|
||||||
|
.demo-card {
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&--wide {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--full {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout helpers
|
||||||
|
.demo-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
&--between {
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
94
src/app/app.component.ts
Normal file
94
src/app/app.component.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {
|
||||||
|
VizBarChartComponent,
|
||||||
|
VizLineChartComponent,
|
||||||
|
VizAreaChartComponent,
|
||||||
|
VizPieChartComponent,
|
||||||
|
VizScatterChartComponent,
|
||||||
|
VizHistogramComponent,
|
||||||
|
VizBoxPlotComponent,
|
||||||
|
VizHeatmapComponent,
|
||||||
|
VizTreemapComponent,
|
||||||
|
VizGaugeComponent,
|
||||||
|
VizSparklineComponent,
|
||||||
|
VizTimeSeriesComponent,
|
||||||
|
VizStatCardComponent,
|
||||||
|
VizProgressRingComponent,
|
||||||
|
VizProgressBarComponent,
|
||||||
|
VizTrendIndicatorComponent,
|
||||||
|
VizDataTableComponent,
|
||||||
|
} from 'data-viz-elements-ui';
|
||||||
|
import {
|
||||||
|
BAR_DATA, LINE_SERIES, PIE_DATA, SCATTER_SERIES,
|
||||||
|
HISTOGRAM_VALUES, BOX_PLOT_DATA, HEATMAP_DATA,
|
||||||
|
HEATMAP_X_LABELS, HEATMAP_Y_LABELS, TREEMAP_DATA,
|
||||||
|
SPARKLINE_DATA, TIME_SERIES_DATA,
|
||||||
|
TABLE_DATA, TABLE_COLUMNS,
|
||||||
|
} from './mock-data/chart-data';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
VizBarChartComponent,
|
||||||
|
VizLineChartComponent,
|
||||||
|
VizAreaChartComponent,
|
||||||
|
VizPieChartComponent,
|
||||||
|
VizScatterChartComponent,
|
||||||
|
VizHistogramComponent,
|
||||||
|
VizBoxPlotComponent,
|
||||||
|
VizHeatmapComponent,
|
||||||
|
VizTreemapComponent,
|
||||||
|
VizGaugeComponent,
|
||||||
|
VizSparklineComponent,
|
||||||
|
VizTimeSeriesComponent,
|
||||||
|
VizStatCardComponent,
|
||||||
|
VizProgressRingComponent,
|
||||||
|
VizProgressBarComponent,
|
||||||
|
VizTrendIndicatorComponent,
|
||||||
|
VizDataTableComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrl: './app.component.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
readonly activeSection = signal<string>('standard');
|
||||||
|
|
||||||
|
// Data
|
||||||
|
readonly barData = BAR_DATA;
|
||||||
|
readonly lineSeries = LINE_SERIES;
|
||||||
|
readonly pieData = PIE_DATA;
|
||||||
|
readonly scatterSeries = SCATTER_SERIES;
|
||||||
|
readonly histogramValues = HISTOGRAM_VALUES;
|
||||||
|
readonly boxPlotData = BOX_PLOT_DATA;
|
||||||
|
readonly heatmapData = HEATMAP_DATA;
|
||||||
|
readonly heatmapXLabels = HEATMAP_X_LABELS;
|
||||||
|
readonly heatmapYLabels = HEATMAP_Y_LABELS;
|
||||||
|
readonly treemapData = TREEMAP_DATA;
|
||||||
|
readonly sparklineData = SPARKLINE_DATA;
|
||||||
|
readonly timeSeriesData = TIME_SERIES_DATA;
|
||||||
|
readonly tableData = TABLE_DATA;
|
||||||
|
readonly tableColumns = TABLE_COLUMNS;
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
readonly sections = [
|
||||||
|
{ id: 'standard', label: 'Standard Charts' },
|
||||||
|
{ id: 'statistical', label: 'Statistical' },
|
||||||
|
{ id: 'realtime', label: 'Real-time & KPI' },
|
||||||
|
{ id: 'table', label: 'Data Table' },
|
||||||
|
];
|
||||||
|
|
||||||
|
setSection(id: string): void {
|
||||||
|
this.activeSection.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDarkMode = signal(false);
|
||||||
|
|
||||||
|
toggleDarkMode(): void {
|
||||||
|
this.isDarkMode.update(v => !v);
|
||||||
|
document.body.setAttribute('data-mode', this.isDarkMode() ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/app/app.config.ts
Normal file
19
src/app/app.config.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
|
import { routes } from './app.routes';
|
||||||
|
import { provideVizConfig } from 'data-viz-elements-ui';
|
||||||
|
|
||||||
|
export const appConfig: ApplicationConfig = {
|
||||||
|
providers: [
|
||||||
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||||
|
provideRouter(routes),
|
||||||
|
provideAnimations(),
|
||||||
|
provideVizConfig({
|
||||||
|
animate: true,
|
||||||
|
animationDuration: 400,
|
||||||
|
responsive: true,
|
||||||
|
tooltips: true,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
};
|
||||||
3
src/app/app.routes.ts
Normal file
3
src/app/app.routes.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
|
export const routes: Routes = [];
|
||||||
185
src/app/mock-data/chart-data.ts
Normal file
185
src/app/mock-data/chart-data.ts
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import type {
|
||||||
|
ChartDataPoint, CartesianDataPoint, ChartSeries,
|
||||||
|
ScatterDataPoint, HeatmapCell, BoxPlotData, TreemapNode,
|
||||||
|
TableColumn,
|
||||||
|
} from 'data-viz-elements-ui';
|
||||||
|
|
||||||
|
// Bar chart data
|
||||||
|
export const BAR_DATA: ChartDataPoint[] = [
|
||||||
|
{ label: 'Jan', value: 45 },
|
||||||
|
{ label: 'Feb', value: 62 },
|
||||||
|
{ label: 'Mar', value: 78 },
|
||||||
|
{ label: 'Apr', value: 55 },
|
||||||
|
{ label: 'May', value: 91 },
|
||||||
|
{ label: 'Jun', value: 67 },
|
||||||
|
{ label: 'Jul', value: 84 },
|
||||||
|
{ label: 'Aug', value: 73 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Line/Area chart series
|
||||||
|
export const LINE_SERIES: ChartSeries[] = [
|
||||||
|
{
|
||||||
|
name: 'Revenue',
|
||||||
|
data: [
|
||||||
|
{ x: 0, y: 30 }, { x: 1, y: 45 }, { x: 2, y: 38 }, { x: 3, y: 52 },
|
||||||
|
{ x: 4, y: 61 }, { x: 5, y: 55 }, { x: 6, y: 72 }, { x: 7, y: 68 },
|
||||||
|
{ x: 8, y: 85 }, { x: 9, y: 78 }, { x: 10, y: 92 }, { x: 11, y: 88 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Expenses',
|
||||||
|
data: [
|
||||||
|
{ x: 0, y: 25 }, { x: 1, y: 32 }, { x: 2, y: 28 }, { x: 3, y: 40 },
|
||||||
|
{ x: 4, y: 35 }, { x: 5, y: 42 }, { x: 6, y: 38 }, { x: 7, y: 45 },
|
||||||
|
{ x: 8, y: 50 }, { x: 9, y: 48 }, { x: 10, y: 55 }, { x: 11, y: 52 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pie chart data
|
||||||
|
export const PIE_DATA: ChartDataPoint[] = [
|
||||||
|
{ label: 'Desktop', value: 45 },
|
||||||
|
{ label: 'Mobile', value: 30 },
|
||||||
|
{ label: 'Tablet', value: 15 },
|
||||||
|
{ label: 'Other', value: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Scatter data
|
||||||
|
export const SCATTER_SERIES: ChartSeries<ScatterDataPoint>[] = [
|
||||||
|
{
|
||||||
|
name: 'Group A',
|
||||||
|
data: Array.from({ length: 20 }, (_, i) => ({
|
||||||
|
x: Math.random() * 100,
|
||||||
|
y: Math.random() * 100,
|
||||||
|
size: Math.random() * 50 + 5,
|
||||||
|
label: `A-${i}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Group B',
|
||||||
|
data: Array.from({ length: 20 }, (_, i) => ({
|
||||||
|
x: Math.random() * 100,
|
||||||
|
y: Math.random() * 100,
|
||||||
|
size: Math.random() * 50 + 5,
|
||||||
|
label: `B-${i}`,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Histogram data
|
||||||
|
export const HISTOGRAM_VALUES: number[] = Array.from({ length: 200 }, () =>
|
||||||
|
Math.random() * 100 + Math.random() * 50
|
||||||
|
);
|
||||||
|
|
||||||
|
// Box plot data
|
||||||
|
export const BOX_PLOT_DATA: BoxPlotData[] = [
|
||||||
|
{ label: 'Q1', min: 10, q1: 25, median: 35, q3: 45, max: 60, outliers: [5, 68] },
|
||||||
|
{ label: 'Q2', min: 15, q1: 30, median: 42, q3: 52, max: 65, outliers: [8] },
|
||||||
|
{ label: 'Q3', min: 20, q1: 35, median: 48, q3: 58, max: 72, outliers: [78] },
|
||||||
|
{ label: 'Q4', min: 12, q1: 28, median: 38, q3: 50, max: 62, outliers: [3, 70] },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Heatmap data
|
||||||
|
const DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||||
|
const HOURS = ['9AM', '10AM', '11AM', '12PM', '1PM', '2PM', '3PM', '4PM', '5PM'];
|
||||||
|
|
||||||
|
export const HEATMAP_DATA: HeatmapCell[] = DAYS.flatMap(day =>
|
||||||
|
HOURS.map(hour => ({
|
||||||
|
x: hour,
|
||||||
|
y: day,
|
||||||
|
value: Math.round(Math.random() * 100),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
export const HEATMAP_X_LABELS = HOURS;
|
||||||
|
export const HEATMAP_Y_LABELS = DAYS;
|
||||||
|
|
||||||
|
// Treemap data
|
||||||
|
export const TREEMAP_DATA: TreemapNode = {
|
||||||
|
name: 'Portfolio',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'Technology',
|
||||||
|
children: [
|
||||||
|
{ name: 'Cloud', value: 45 },
|
||||||
|
{ name: 'AI/ML', value: 35 },
|
||||||
|
{ name: 'Security', value: 20 },
|
||||||
|
{ name: 'DevOps', value: 15 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Finance',
|
||||||
|
children: [
|
||||||
|
{ name: 'Banking', value: 30 },
|
||||||
|
{ name: 'Insurance', value: 25 },
|
||||||
|
{ name: 'Crypto', value: 10 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Healthcare',
|
||||||
|
children: [
|
||||||
|
{ name: 'Pharma', value: 28 },
|
||||||
|
{ name: 'MedTech', value: 18 },
|
||||||
|
{ name: 'Digital Health', value: 12 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sparkline data
|
||||||
|
export const SPARKLINE_DATA: number[] = [4, 8, 5, 10, 7, 12, 9, 15, 11, 18, 14, 20];
|
||||||
|
|
||||||
|
// Time series data
|
||||||
|
function generateTimeData(points: number, baseValue: number): CartesianDataPoint[] {
|
||||||
|
const now = Date.now();
|
||||||
|
const result: CartesianDataPoint[] = [];
|
||||||
|
let value = baseValue;
|
||||||
|
for (let i = 0; i < points; i++) {
|
||||||
|
value += (Math.random() - 0.48) * 5;
|
||||||
|
value = Math.max(0, value);
|
||||||
|
result.push({ x: new Date(now - (points - i) * 60000), y: Math.round(value * 10) / 10 });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TIME_SERIES_DATA: ChartSeries[] = [
|
||||||
|
{ name: 'CPU Usage', data: generateTimeData(60, 45) },
|
||||||
|
{ name: 'Memory', data: generateTimeData(60, 65) },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Data table
|
||||||
|
export interface SalesRecord {
|
||||||
|
id: number;
|
||||||
|
product: string;
|
||||||
|
category: string;
|
||||||
|
revenue: number;
|
||||||
|
units: number;
|
||||||
|
region: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TABLE_COLUMNS: TableColumn<SalesRecord>[] = [
|
||||||
|
{ key: 'id', label: 'ID', width: '60px', sortable: true },
|
||||||
|
{ key: 'product', label: 'Product', sortable: true, filterable: true },
|
||||||
|
{ key: 'category', label: 'Category', sortable: true, filterable: true },
|
||||||
|
{ key: 'revenue', label: 'Revenue', sortable: true, align: 'right', format: (v) => `$${Number(v).toLocaleString()}` },
|
||||||
|
{ key: 'units', label: 'Units', sortable: true, align: 'right' },
|
||||||
|
{ key: 'region', label: 'Region', sortable: true, filterable: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TABLE_DATA: SalesRecord[] = [
|
||||||
|
{ id: 1, product: 'Widget Pro', category: 'Hardware', revenue: 12500, units: 250, region: 'North' },
|
||||||
|
{ id: 2, product: 'DataSync', category: 'Software', revenue: 34200, units: 120, region: 'South' },
|
||||||
|
{ id: 3, product: 'CloudBox', category: 'Services', revenue: 28900, units: 85, region: 'East' },
|
||||||
|
{ id: 4, product: 'SecureNet', category: 'Software', revenue: 41000, units: 200, region: 'West' },
|
||||||
|
{ id: 5, product: 'IoT Hub', category: 'Hardware', revenue: 18700, units: 150, region: 'North' },
|
||||||
|
{ id: 6, product: 'Analytics+', category: 'Software', revenue: 52300, units: 310, region: 'South' },
|
||||||
|
{ id: 7, product: 'EdgeNode', category: 'Hardware', revenue: 9800, units: 60, region: 'East' },
|
||||||
|
{ id: 8, product: 'StreamAPI', category: 'Services', revenue: 23400, units: 95, region: 'West' },
|
||||||
|
{ id: 9, product: 'VaultKey', category: 'Software', revenue: 31500, units: 175, region: 'North' },
|
||||||
|
{ id: 10, product: 'NetBridge', category: 'Hardware', revenue: 15600, units: 110, region: 'South' },
|
||||||
|
{ id: 11, product: 'FlowEngine', category: 'Software', revenue: 47800, units: 260, region: 'East' },
|
||||||
|
{ id: 12, product: 'SensorPack', category: 'Hardware', revenue: 11200, units: 80, region: 'West' },
|
||||||
|
{ id: 13, product: 'CloudMigrate', category: 'Services', revenue: 39500, units: 45, region: 'North' },
|
||||||
|
{ id: 14, product: 'PipelineCI', category: 'Software', revenue: 26700, units: 190, region: 'South' },
|
||||||
|
{ id: 15, product: 'MonitorPro', category: 'Services', revenue: 33100, units: 140, region: 'East' },
|
||||||
|
];
|
||||||
14
src/index.html
Normal file
14
src/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Data Viz Elements Demo</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="description" content="Interactive demo for data-viz-elements-ui library">
|
||||||
|
<link rel="icon" type="image/x-icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📊</text></svg>">
|
||||||
|
</head>
|
||||||
|
<body data-theme="mist" data-mode="light">
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
6
src/main.ts
Normal file
6
src/main.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { bootstrapApplication } from '@angular/platform-browser';
|
||||||
|
import { appConfig } from './app/app.config';
|
||||||
|
import { AppComponent } from './app/app.component';
|
||||||
|
|
||||||
|
bootstrapApplication(AppComponent, appConfig)
|
||||||
|
.catch((err) => console.error(err));
|
||||||
68
src/styles.scss
Normal file
68
src/styles.scss
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Global reset
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root styles
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background: var(--color-bg-primary, #f9fafb);
|
||||||
|
color: var(--color-text-primary, #111827);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Light mode
|
||||||
|
[data-mode="light"] {
|
||||||
|
--color-bg-primary: #f9fafb;
|
||||||
|
--color-bg-secondary: #ffffff;
|
||||||
|
--color-text-primary: #111827;
|
||||||
|
--color-text-secondary: #6b7280;
|
||||||
|
--color-border: #e5e7eb;
|
||||||
|
--color-primary: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode
|
||||||
|
[data-mode="dark"] {
|
||||||
|
--color-bg-primary: #111827;
|
||||||
|
--color-bg-secondary: #1f2937;
|
||||||
|
--color-text-primary: #f9fafb;
|
||||||
|
--color-text-secondary: #9ca3af;
|
||||||
|
--color-border: #374151;
|
||||||
|
--color-primary: #60a5fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrollbar styling
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus visible styles
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
13
tsconfig.app.json
Normal file
13
tsconfig.app.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./out-tsc/app",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/main.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
30
tsconfig.json
Normal file
30
tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/out-tsc",
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": false,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"importHelpers": true,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ES2022",
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"dom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableI18nLegacyMessageIdFormat": false,
|
||||||
|
"strictInjectionParameters": true,
|
||||||
|
"strictInputAccessModifiers": true,
|
||||||
|
"strictTemplates": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user