https://unovis.dev/ Skip to main content UnovisUnovis DocsGallery Source Code [unovis-log] A modular data visualization framework for React, Angular, Svelte, and vanilla TypeScript or JavaScript Get Started Framework Independent Framework Independent Use with React, Angular, Svelte, or without any UI framework. Smooth Smooth We pay attention to how the library components look and how they react to data changes. Styles are customizable via CSS variables. Built with Typescript Built with Typescript Unovis is built with Typescript and allows you to import individual component modules to reduce your app bundle size. Unovis in action See more examples in Gallery -> * React * Angular * Svelte * TypeScript * Data import React, { useCallback } from 'react' import { VisXYContainer, VisAxis, VisArea, VisXYLabels } from '@unovis/react' import { data, formats, DataRecord, getLabels } from './data' export default function StackedArea (): JSX.Element { const labels = getLabels(data) return ( <> d.year, [])} y={formats.map(g => useCallback((d: DataRecord) => d[g], []))}/> labels[d.year] ? d.year : undefined, [])} y={useCallback((d: DataRecord) => labels[d.year]?.value, [])} label={useCallback((d: DataRecord) => labels[d.year]?.label, [])} backgroundColor={useCallback((d: DataRecord) => labels[d.year]?.color ?? 'none', [])} clusterBackgroundColor="none" clusterLabel={() => ''} /> ) } stacked-area-chart-with-xy-labels.html stacked-area-chart-with-xy-labels.component.ts import { Component } from '@angular/core' import { data, formats, DataRecord, getLabels } from './data' @Component({ selector: 'stacked-area-chart', templateUrl: './stacked-area-chart.component.html', }) export class StackedAreaComponent { data = data x = (d: DataRecord): number => d.year y = formats.map(f => (d: DataRecord) => d[f]) labelItems = getLabels(this.data) labelY = (d: DataRecord): number => this.labelItems[d.year]?.value ?? 0 labelText = (d: DataRecord): string => this.labelItems[d.year]?.label ?? '' labelColor = (d: DataRecord): string => this.labelItems[d.year]?.color ?? 'none' noLabel = (): string => '' } stacked-area-chart-with-xy-labels.module.ts import { NgModule } from '@angular/core' import { VisXYContainerModule, VisAreaModule, VisXYLabelsModule, VisAxisModule } from '@unovis/angular' import { StackedAreaComponent } from './stacked-area-chart.component' @NgModule({ imports: [VisXYContainerModule, VisAreaModule, VisXYLabelsModule, VisAxisModule], declarations: [StackedAreaComponent], exports: [StackedAreaComponent], }) export class StackedAreaModule { } ''}/> import { Area, Axis, XYContainer, XYLabels } from '@unovis/ts' import { data, formats, DataRecord, getLabels } from './data' const labels = getLabels(data) const container = document.getElementById('vis-container') const chart = new XYContainer(container, { height: 500, components: [ // area chart new Area({ x: (d: DataRecord) => d.year, y: formats.map(f => (d: DataRecord) => d[f]), }), // labels new XYLabels({ x: (d: DataRecord) => labels[d.year] ? d.year : undefined, y: (d: DataRecord) => labels[d.year]?.value, label: (d: DataRecord) => labels[d.year]?.label ?? '', backgroundColor: (d: DataRecord) => labels[d.year]?.color, clusterBackgroundColor: 'none', clusterLabel: () => '', }), ], xAxis: new Axis({ label: 'Year', numTicks: 10, domainLine: false, gridLine: false }), yAxis: new Axis({ label: 'Revenue (USD, billions)', numTicks: 10 }), }, data) enum Format { Vinyl = 'vinyl', Cassette = 'cassette', Cd = 'cd', Download = 'download', Streaming = 'streaming', } export const formats: Format[] = [Format.Vinyl, Format.Cassette, Format.Cd, Format.Download, Format.Streaming] export type DataRecord = Record & { year: number }; export type Label = { label: string; value: number; color: string; } export function getMaxItems> ( array: T[], keys: (keyof T)[] ): { [key in keyof T]?: T } { const maxIndex = (k: keyof T): number => array.reduce((max, curr, i) => curr[k] > array[max][k] ? i : max, 0) const entries = keys.map(key => [ key, array[maxIndex(key)], ]) return Object.fromEntries(entries) } export function getLabels (data: DataRecord[]): Record { // map formats to their maximum data records const peakItems = getMaxItems(data.slice(0, data.length - 3), formats) // place labels at [x,y] where x = peak year and y = area midpoint return formats.reduce((obj, format, i) => { const offset = Array(i).fill(0).reduce((sum, _, j) => sum + peakItems[format][formats[j]], 0) const [x, y] = [peakItems[format].year, offset + peakItems[format][format] / 2] obj[x] = { label: format === 'cd' ? format.toUpperCase() : format.charAt(0).toUpperCase() + format.slice(1), value: y, color: 'none', } return obj }, {}) } export const data: DataRecord[] = [ { year: 1973, vinyl: 1.436, cd: 0, streaming: 0, cassette: 0.5806, download: 0, }, { year: 1974, vinyl: 1.55, cd: 0, streaming: 0, cassette: 0.6497, download: 0, }, { year: 1975, vinyl: 1.6965, cd: 0, streaming: 0, cassette: 0.692, download: 0, }, { year: 1976, vinyl: 1.9081, cd: 0, streaming: 0, cassette: 0.829, download: 0, }, { year: 1977, vinyl: 2.4402, cd: 0, streaming: 0, cassette: 1.0606, download: 0, }, { year: 1978, vinyl: 2.7336, cd: 0, streaming: 0, cassette: 1.3978, download: 0, }, { year: 1979, vinyl: 2.4106, cd: 0, streaming: 0, cassette: 1.2649, download: 0, }, { year: 1980, vinyl: 2.45, cd: 0, streaming: 0, cassette: 1.232, download: 0, }, { year: 1981, vinyl: 2.5981, cd: 0, streaming: 0, cassette: 1.3758, download: 0, }, { year: 1982, vinyl: 2.2081, cd: 0, streaming: 0, cassette: 1.4205, download: 0, }, { year: 1983, vinyl: 1.9583, cd: 0.0172, streaming: 0, cassette: 1.8109, download: 0, }, { year: 1984, vinyl: 1.8475, cd: 0.1033, streaming: 0, cassette: 2.3839, download: 0, }, { year: 1985, vinyl: 1.5615, cd: 0.3895, streaming: 0, cassette: 2.4115, download: 0, }, { year: 1986, vinyl: 1.2111, cd: 0.9301, streaming: 0, cassette: 2.4995, download: 0, }, { year: 1987, vinyl: 0.9964, cd: 1.5936, streaming: 0, cassette: 2.974, download: 0, }, { year: 1988, vinyl: 0.7126, cd: 2.0997, streaming: 0, cassette: 3.4424, download: 0, }, { year: 1989, vinyl: 0.3367, cd: 2.7024, streaming: 0, cassette: 3.5404, download: 0, }, { year: 1990, vinyl: 0.1809, cd: 3.6299, streaming: 0, cassette: 3.7303, download: 0, }, { year: 1991, vinyl: 0.0933, cd: 4.4909, streaming: 0, cassette: 3.25, download: 0, }, { year: 1992, vinyl: 0.0799, cd: 5.529, streaming: 0, cassette: 3.4151, download: 0, }, { year: 1993, vinyl: 0.0618, cd: 6.7705, streaming: 0, cassette: 3.2143, download: 0, }, { year: 1994, vinyl: 0.065, cd: 8.7517, streaming: 0, cassette: 3.2513, download: 0, }, { year: 1995, vinyl: 0.0718, cd: 9.7086, streaming: 0, cassette: 2.5399, download: 0, }, { year: 1996, vinyl: 0.0843, cd: 10.3549, streaming: 0, cassette: 2.0946, download: 0, }, { year: 1997, vinyl: 0.0689, cd: 10.5117, streaming: 0, cassette: 1.6562, download: 0, }, { year: 1998, vinyl: 0.0597, cd: 12.1372, streaming: 0, cassette: 1.5143, download: 0, }, { year: 1999, vinyl: 0.0597, cd: 13.4154, streaming: 0, cassette: 1.1096, download: 0, }, { year: 2000, vinyl: 0.054, cd: 13.6391, streaming: 0, cassette: 0.6306, download: 0, }, { year: 2001, vinyl: 0.0588, cd: 13.324, streaming: 0, cassette: 0.3581, download: 0, }, { year: 2002, vinyl: 0.0454, cd: 12.3606, streaming: 0, cassette: 0.2082, download: 0, }, { year: 2003, vinyl: 0.0432, cd: 11.7031, streaming: 0, cassette: 0.1081, download: 0, }, { year: 2004, vinyl: 0.0392, cd: 12.0918, streaming: 0.0069, cassette: 0.0237, download: 0.1835, }, { year: 2005, vinyl: 0.0274, cd: 11.1545, streaming: 0.1709, cassette: 0.0131, download: 0.9243, }, { year: 2006, vinyl: 0.0256, cd: 9.8393, streaming: 0.2407, cassette: 0.0037, download: 1.65, }, { year: 2007, vinyl: 0.0269, cd: 7.9558, streaming: 0.272, cassette: 0.003, download: 2.3924, }, { year: 2008, vinyl: 0.0596, cd: 5.7064, streaming: 0.323, cassette: 0.0009, download: 2.6859, }, { year: 2009, vinyl: 0.0663, cd: 4.5355, streaming: 0.3629, cassette: 0.0, download: 2.66, }, { year: 2010, vinyl: 0.0912, cd: 3.5725, streaming: 0.4631, cassette: 0.0, download: 2.6934, }, { year: 2011, vinyl: 0.124, cd: 3.257, streaming: 0.6554, cassette: 0.0, download: 2.9018, }, { year: 2012, vinyl: 0.1655, cd: 2.607, streaming: 1.0362, cassette: 0, download: 3.0162, }, { year: 2013, vinyl: 0.2137, cd: 2.2501, streaming: 1.4608, cassette: 0, download: 2.9203, }, { year: 2014, vinyl: 0.2493, cd: 1.8725, streaming: 1.8352, cassette: 0, download: 2.5531, }, { year: 2015, vinyl: 0.3391, cd: 1.5231, streaming: 2.3421, cassette: 0, download: 2.3107, }, { year: 2016, vinyl: 0.3603, cd: 1.192, streaming: 4.0019, cassette: 0, download: 1.8294, }, { year: 2017, vinyl: 0.3946, cd: 1.1009, streaming: 5.7167, cassette: 0, download: 1.3853, }, { year: 2018, vinyl: 0.4245, cd: 0.7303, streaming: 7.4368, cassette: 0, download: 1.0173, }, { year: 2019, vinyl: 0.5044, cd: 0.6439, streaming: 8.9132, cassette: 0, download: 0.8326, }, ] Companies using Unovis F5 LogoF5 LogoNGIX LogoNGIX Logo Learn * Quick Start * Gallery Community * GitHub Discussions * StackOverflow * Twitter More * Source Code Copyright (c) 2022 F5, Inc.