diff --git a/packages/angular/src/charts.module.ts b/packages/angular/src/charts.module.ts index f1e49fc892..2174a506e4 100644 --- a/packages/angular/src/charts.module.ts +++ b/packages/angular/src/charts.module.ts @@ -9,6 +9,7 @@ import { GroupedBarChartComponent } from "./bar-chart-grouped.component"; import { StackedBarChartComponent } from "./bar-chart-stacked.component"; import { BubbleChartComponent } from "./bubble-chart.component"; import { DonutChartComponent } from "./donut-chart.component"; +import { GaugeChartComponent } from "./gauge-chart.component"; import { LineChartComponent } from "./line-chart.component"; import { PieChartComponent } from "./pie-chart.component"; import { ScatterChartComponent } from "./scatter-chart.component"; @@ -27,6 +28,7 @@ import { RadarChartComponent } from "./radar-chart.component"; StackedBarChartComponent, BubbleChartComponent, DonutChartComponent, + GaugeChartComponent, LineChartComponent, PieChartComponent, ScatterChartComponent, @@ -41,6 +43,7 @@ import { RadarChartComponent } from "./radar-chart.component"; StackedBarChartComponent, BubbleChartComponent, DonutChartComponent, + GaugeChartComponent, LineChartComponent, PieChartComponent, ScatterChartComponent, diff --git a/packages/angular/src/gauge-chart.component.ts b/packages/angular/src/gauge-chart.component.ts new file mode 100644 index 0000000000..2b56959bad --- /dev/null +++ b/packages/angular/src/gauge-chart.component.ts @@ -0,0 +1,34 @@ +import { + Component, + AfterViewInit +} from "@angular/core"; + +import { BaseChart } from "./base-chart.component"; + +import { GaugeChart } from "@carbon/charts"; + +/** + * Wrapper around `GaugeChart` in carbon charts library + * + * Most functions just call their equivalent from the chart library. + */ +@Component({ + selector: "ibm-gauge-chart", + template: `` +}) +export class GaugeChartComponent extends BaseChart implements AfterViewInit { + /** + * Runs after view init to create a chart, attach it to `elementRef` and draw it. + */ + ngAfterViewInit() { + this.chart = new GaugeChart( + this.elementRef.nativeElement, + { + data: this.data, + options: this.options + } + ); + + Object.assign(this, this.chart); + } +} diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index fd19c857a7..39a4ce0c2b 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -7,6 +7,7 @@ export * from "./bar-chart-grouped.component"; export * from "./bar-chart-stacked.component"; export * from "./bubble-chart.component"; export * from "./donut-chart.component"; +export * from "./gauge-chart.component"; export * from "./line-chart.component"; export * from "./pie-chart.component"; export * from "./scatter-chart.component"; diff --git a/packages/core/demo/data/area.ts b/packages/core/demo/data/area.ts index 75f2bad200..2556a61139 100644 --- a/packages/core/demo/data/area.ts +++ b/packages/core/demo/data/area.ts @@ -13,7 +13,7 @@ export const areaTimeSeriesData = [ { group: "Dataset 3", date: new Date(2019, 0, 6), value: 37312 }, { group: "Dataset 3", date: new Date(2019, 0, 8), value: 51432 }, { group: "Dataset 3", date: new Date(2019, 0, 13), value: 40323 }, - { group: "Dataset 3", date: new Date(2019, 0, 19), value: 31300 }, + { group: "Dataset 3", date: new Date(2019, 0, 19), value: 31300 } ]; export const areaTimeSeriesOptions = { @@ -22,14 +22,14 @@ export const areaTimeSeriesOptions = { bottom: { title: "2019 Annual Sales Figures", mapsTo: "date", - scaleType: "time", + scaleType: "time" }, left: { mapsTo: "value", title: "Conversion rate", - scaleType: "linear", - }, - }, + scaleType: "linear" + } + } }; export const areaTimeSeriesCurvedData = [ @@ -42,7 +42,7 @@ export const areaTimeSeriesCurvedData = [ { group: "Dataset 2", date: new Date(2019, 0, 5), value: 14178 }, { group: "Dataset 2", date: new Date(2019, 0, 8), value: 23094 }, { group: "Dataset 2", date: new Date(2019, 0, 13), value: 45281 }, - { group: "Dataset 2", date: new Date(2019, 0, 19), value: -63954 }, + { group: "Dataset 2", date: new Date(2019, 0, 19), value: -63954 } ]; export const areaTimeSeriesCurvedOptions = { @@ -51,14 +51,14 @@ export const areaTimeSeriesCurvedOptions = { bottom: { title: "2019 Annual Sales Figures", mapsTo: "date", - scaleType: "time", + scaleType: "time" }, left: { mapsTo: "value", - scaleType: "linear", - }, + scaleType: "linear" + } }, - curve: "curveNatural", + curve: "curveNatural" }; export const stackedAreaTimeSeriesData = [ @@ -76,20 +76,20 @@ export const stackedAreaTimeSeriesData = [ { group: "Dataset 3", date: new Date(2019, 0, 5), value: 20000 }, { group: "Dataset 3", date: new Date(2019, 0, 8), value: 40000 }, { group: "Dataset 3", date: new Date(2019, 0, 13), value: 60213 }, - { group: "Dataset 3", date: new Date(2019, 0, 17), value: 25213 }, + { group: "Dataset 3", date: new Date(2019, 0, 17), value: 25213 } ]; export const stackedAreaTimeSeriesOptions = { title: "Stacked area (time series)", axes: { left: { - stacked: true, + stacked: true }, bottom: { - scaleType: "time", - }, + scaleType: "time" + } }, - curve: "curveMonotoneX", + curve: "curveMonotoneX" }; export const stackedAreaPercentageTimeSeriesOptions = { @@ -99,12 +99,12 @@ export const stackedAreaPercentageTimeSeriesOptions = { stacked: true, percentage: true, ticks: { - formatter: (d) => `${d}%`, - }, + formatter: (d) => `${d}%` + } }, bottom: { - scaleType: "time", - }, + scaleType: "time" + } }, - curve: "curveMonotoneX", + curve: "curveMonotoneX" }; diff --git a/packages/core/demo/data/donut.ts b/packages/core/demo/data/donut.ts index c88007c2c0..ae6eae6cf4 100644 --- a/packages/core/demo/data/donut.ts +++ b/packages/core/demo/data/donut.ts @@ -7,9 +7,9 @@ export const donutOptions = { resizable: true, donut: { center: { - label: "Browsers", - }, - }, + label: "Browsers" + } + } }; // donut - empty state @@ -19,9 +19,9 @@ export const donutEmptyStateOptions = { resizable: true, donut: { center: { - label: "Browsers", - }, - }, + label: "Browsers" + } + } }; // donut - skeleton @@ -31,10 +31,10 @@ export const donutSkeletonOptions = { resizable: true, donut: { center: { - label: "Browsers", - }, + label: "Browsers" + } }, data: { - loading: true, - }, + loading: true + } }; diff --git a/packages/core/demo/data/gauge.ts b/packages/core/demo/data/gauge.ts new file mode 100644 index 0000000000..a293aebaba --- /dev/null +++ b/packages/core/demo/data/gauge.ts @@ -0,0 +1,44 @@ +export const gaugeData = [ + { group: "value", value: 42 }, + { group: "delta", value: -13.37 } +]; + +export const gaugeDataNoDelta = [{ group: "value", value: 67 }]; + +// guage no custom color +export const gaugeOptionsSemi = { + title: "Gauge semicircular -- danger status", + resizable: true, + height: "250px", + width: "100%", + gauge: { + type: "semi", + status: "danger" + } +}; + +// guage with custom color +export const gaugeOptionsCircular = { + title: "Gauge circular -- warning status", + resizable: true, + height: "250px", + gauge: { + status: "warning", + type: "full" + } +}; + +// guage with custom color +export const gaugeOptionsCircularNoDelta = { + title: "Gauge circular without delta", + resizable: true, + height: "250px", + gauge: { + type: "full" + }, + color: { + scale: { + value: "#891EE8" + } + } +}; diff --git a/packages/core/demo/data/index.ts b/packages/core/demo/data/index.ts index d83931e319..09d25f8246 100644 --- a/packages/core/demo/data/index.ts +++ b/packages/core/demo/data/index.ts @@ -4,6 +4,7 @@ import * as bubbleDemos from "./bubble"; import * as donutDemos from "./donut"; import * as lineDemos from "./line"; import * as pieDemos from "./pie"; +import * as gaugeDemos from "./gauge"; import * as scatterDemos from "./scatter"; import * as stepDemos from "./step"; import * as timeSeriesAxisDemos from "./time-series-axis"; @@ -15,6 +16,7 @@ export * from "./bubble"; export * from "./donut"; export * from "./line"; export * from "./pie"; +export * from "./gauge"; export * from "./scatter"; export * from "./step"; export * from "./radar"; @@ -25,7 +27,7 @@ import { createAngularChartApp, createVueChartApp, createVanillaChartApp, - createSvelteChartApp, + createSvelteChartApp } from "../create-codesandbox"; import { Tools } from "@carbon/charts/tools"; @@ -34,58 +36,63 @@ export const chartTypes = { AreaChart: { vanilla: "AreaChart", angular: "ibm-area-chart", - vue: "ccv-area-chart", + vue: "ccv-area-chart" }, SimpleBarChart: { vanilla: "SimpleBarChart", angular: "ibm-simple-bar-chart", - vue: "ccv-simple-bar-chart", + vue: "ccv-simple-bar-chart" }, GroupedBarChart: { vanilla: "GroupedBarChart", angular: "ibm-grouped-bar-chart", - vue: "ccv-grouped-bar-chart", + vue: "ccv-grouped-bar-chart" }, StackedBarChart: { vanilla: "StackedBarChart", angular: "ibm-stacked-bar-chart", - vue: "ccv-stacked-bar-chart", + vue: "ccv-stacked-bar-chart" }, BubbleChart: { vanilla: "BubbleChart", angular: "ibm-bubble-chart", - vue: "ccv-bubble-chart", + vue: "ccv-bubble-chart" }, LineChart: { vanilla: "LineChart", angular: "ibm-line-chart", - vue: "ccv-line-chart", + vue: "ccv-line-chart" }, StackedAreaChart: { vanilla: "StackedAreaChart", angular: "ibm-stacked-area-chart", - vue: "ccv-stacked-area-chart", + vue: "ccv-stacked-area-chart" }, ScatterChart: { vanilla: "ScatterChart", angular: "ibm-scatter-chart", - vue: "ccv-scatter-chart", + vue: "ccv-scatter-chart" }, PieChart: { vanilla: "PieChart", angular: "ibm-pie-chart", - vue: "ccv-pie-chart", + vue: "ccv-pie-chart" + }, + GaugeChart: { + vanilla: "GaugeChart", + angular: "ibm-gauge-chart", + vue: "ccv-gauge-chart" }, DonutChart: { vanilla: "DonutChart", angular: "ibm-donut-chart", - vue: "ccv-donut-chart", + vue: "ccv-donut-chart" }, RadarChart: { vanilla: "RadarChart", angular: "ibm-radar-chart", - vue: "ccv-radar-chart", - }, + vue: "ccv-radar-chart" + } }; let allDemoGroups = [ @@ -97,26 +104,26 @@ let allDemoGroups = [ { options: areaDemos.areaTimeSeriesCurvedOptions, data: areaDemos.areaTimeSeriesCurvedData, - chartType: chartTypes.AreaChart, + chartType: chartTypes.AreaChart }, { options: areaDemos.areaTimeSeriesOptions, data: areaDemos.areaTimeSeriesData, - chartType: chartTypes.AreaChart, + chartType: chartTypes.AreaChart }, { options: areaDemos.stackedAreaTimeSeriesOptions, data: areaDemos.stackedAreaTimeSeriesData, chartType: chartTypes.StackedAreaChart, - isDemoExample: true, + isDemoExample: true }, { options: areaDemos.stackedAreaPercentageTimeSeriesOptions, data: areaDemos.stackedAreaTimeSeriesData, chartType: chartTypes.StackedAreaChart, - isDemoExample: true, - }, - ], + isDemoExample: true + } + ] }, { title: "Bar (vertical)", @@ -126,74 +133,74 @@ let allDemoGroups = [ { options: barDemos.simpleBarOptions, data: barDemos.simpleBarData, - chartType: chartTypes.SimpleBarChart, + chartType: chartTypes.SimpleBarChart }, { options: barDemos.simpleBarTimeSeriesOptions, data: barDemos.simpleBarTimeSeriesData, - chartType: chartTypes.SimpleBarChart, + chartType: chartTypes.SimpleBarChart }, { options: barDemos.simpleBarFixedDomainOptions, data: barDemos.simpleBarData, - chartType: chartTypes.SimpleBarChart, + chartType: chartTypes.SimpleBarChart }, { options: barDemos.simpleBarEmptyStateOptions, data: barDemos.simpleBarEmptyStateData, chartType: chartTypes.SimpleBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.simpleBarSkeletonOptions, data: barDemos.simpleBarSkeletonData, chartType: chartTypes.SimpleBarChart, - isDemoExample: false, + isDemoExample: false }, { description: "A grouped bar chart, also known as a clustered bar graph, multi-set bar chart, or grouped column chart, is a type of bar graph that is used to compare values across multiple categories.", options: barDemos.groupedBarOptions, data: barDemos.groupedBarData, - chartType: chartTypes.GroupedBarChart, + chartType: chartTypes.GroupedBarChart }, { options: barDemos.groupedBarEmptyStateOptions, data: barDemos.groupedBarEmptyStateData, chartType: chartTypes.GroupedBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.groupedBarSkeletonOptions, data: barDemos.groupedBarSkeletonData, chartType: chartTypes.GroupedBarChart, - isDemoExample: false, + isDemoExample: false }, { description: "Stacked bar charts are useful for comparing proportional contributions within a category. They plot the relative value that each data series contributes to the total.", options: barDemos.stackedBarOptions, data: barDemos.stackedBarData, - chartType: chartTypes.StackedBarChart, + chartType: chartTypes.StackedBarChart }, { options: barDemos.stackedBarTimeSeriesOptions, data: barDemos.stackedBarTimeSeriesData, - chartType: chartTypes.StackedBarChart, + chartType: chartTypes.StackedBarChart }, { options: barDemos.stackedBarEmptyStateOptions, data: barDemos.stackedBarEmptyStateData, chartType: chartTypes.StackedBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.stackedBarSkeletonOptions, data: barDemos.stackedBarSkeletonData, chartType: chartTypes.StackedBarChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Bar (horizontal)", @@ -201,65 +208,65 @@ let allDemoGroups = [ { options: barDemos.simpleHorizontalBarOptions, data: barDemos.simpleHorizontalBarData, - chartType: chartTypes.SimpleBarChart, + chartType: chartTypes.SimpleBarChart }, { options: barDemos.simpleHorizontalBarTimeSeriesOptions, data: barDemos.simpleHorizontalBarTimeSeriesData, - chartType: chartTypes.SimpleBarChart, + chartType: chartTypes.SimpleBarChart }, { options: barDemos.simpleHorizontalBarEmptyStateOptions, data: barDemos.simpleHorizontalBarEmptyStateData, chartType: chartTypes.SimpleBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.simpleHorizontalBarSkeletonOptions, data: barDemos.simpleHorizontalBarSkeletonData, chartType: chartTypes.SimpleBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.groupedHorizontalBarOptions, data: barDemos.groupedHorizontalBarData, - chartType: chartTypes.GroupedBarChart, + chartType: chartTypes.GroupedBarChart }, { options: barDemos.groupedHorizontalBarEmptyStateOptions, data: barDemos.groupedHorizontalBarEmptyStateData, chartType: chartTypes.GroupedBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.groupedHorizontalBarSkeletonOptions, data: barDemos.groupedHorizontalBarSkeletonData, chartType: chartTypes.GroupedBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.stackedHorizontalBarOptions, data: barDemos.stackedHorizontalBarData, - chartType: chartTypes.StackedBarChart, + chartType: chartTypes.StackedBarChart }, { options: barDemos.stackedHorizontalBarTimeSeriesOptions, data: barDemos.stackedHorizontalBarTimeSeriesData, - chartType: chartTypes.StackedBarChart, + chartType: chartTypes.StackedBarChart }, { options: barDemos.stackedHorizontalBarEmptyStateOptions, data: barDemos.stackedHorizontalBarEmptyStateData, chartType: chartTypes.StackedBarChart, - isDemoExample: false, + isDemoExample: false }, { options: barDemos.stackedHorizontalBarSkeletonOptions, data: barDemos.stackedHorizontalBarSkeletonData, chartType: chartTypes.StackedBarChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Bubble", @@ -269,31 +276,31 @@ let allDemoGroups = [ { options: bubbleDemos.bubbleDoubleLinearOptions, data: bubbleDemos.bubbleDoubleLinearData, - chartType: chartTypes.BubbleChart, + chartType: chartTypes.BubbleChart }, { options: bubbleDemos.bubbleTimeSeriesOptions, data: bubbleDemos.bubbleTimeSeriesData, - chartType: chartTypes.BubbleChart, + chartType: chartTypes.BubbleChart }, { options: bubbleDemos.bubbleDiscreteOptions, data: bubbleDemos.bubbleDiscreteData, - chartType: chartTypes.BubbleChart, + chartType: chartTypes.BubbleChart }, { options: bubbleDemos.bubbleEmptyStateOptions, data: bubbleDemos.bubbleEmptyStateData, chartType: chartTypes.BubbleChart, - isDemoExample: false, + isDemoExample: false }, { options: bubbleDemos.bubbleSkeletonOptions, data: bubbleDemos.bubbleSkeletonData, chartType: chartTypes.BubbleChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Donut", @@ -301,21 +308,21 @@ let allDemoGroups = [ { options: donutDemos.donutOptions, data: donutDemos.donutData, - chartType: chartTypes.DonutChart, + chartType: chartTypes.DonutChart }, { options: donutDemos.donutEmptyStateOptions, data: donutDemos.donutEmptyStateData, chartType: chartTypes.DonutChart, - isDemoExample: false, + isDemoExample: false }, { options: donutDemos.donutSkeletonOptions, data: donutDemos.donutSkeletonData, chartType: chartTypes.DonutChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Line", @@ -325,7 +332,7 @@ let allDemoGroups = [ { options: lineDemos.lineTimeSeriesOptions, data: lineDemos.lineTimeSeriesData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: lineDemos.lineTimeSeriesCustomDomainOptions, @@ -335,12 +342,12 @@ let allDemoGroups = [ { options: lineDemos.lineTimeSeriesDenseOptions, data: lineDemos.lineTimeSeriesDenseData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: lineDemos.lineOptions, data: lineDemos.lineData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: lineDemos.lineCustomDomainOptions, @@ -351,38 +358,38 @@ let allDemoGroups = [ options: lineDemos.lineTimeSeriesRotatedTicksOptions, data: lineDemos.lineTimeSeriesDataRotatedTicks, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: lineDemos.lineTimeSeriesHorizontalOptions, data: lineDemos.lineTimeSeriesData, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: lineDemos.lineHorizontalOptions, data: lineDemos.lineData, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: lineDemos.lineTimeSeriesWithThresholdsOptions, data: lineDemos.lineTimeSeriesData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: lineDemos.lineEmptyStateOptions, data: lineDemos.lineEmptyStateData, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: lineDemos.lineSkeletonOptions, data: lineDemos.lineSkeletonData, chartType: chartTypes.LineChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Pie", @@ -390,21 +397,44 @@ let allDemoGroups = [ { options: pieDemos.pieOptions, data: pieDemos.pieData, - chartType: chartTypes.PieChart, + chartType: chartTypes.PieChart }, { options: pieDemos.pieEmptyStateOptions, data: pieDemos.pieEmptyStateData, chartType: chartTypes.PieChart, - isDemoExample: false, + isDemoExample: false }, { options: pieDemos.pieSkeletonOptions, data: pieDemos.pieSkeletonData, chartType: chartTypes.PieChart, - isDemoExample: false, + isDemoExample: false + } + ] + }, + { + title: "Gauge", + demos: [ + { + options: gaugeDemos.gaugeOptionsSemi, + data: gaugeDemos.gaugeData, + chartType: chartTypes.GaugeChart, + isDemoExample: true + }, + { + options: gaugeDemos.gaugeOptionsCircular, + data: gaugeDemos.gaugeData, + chartType: chartTypes.GaugeChart, + isDemoExample: true }, - ], + { + options: gaugeDemos.gaugeOptionsCircularNoDelta, + data: gaugeDemos.gaugeDataNoDelta, + chartType: chartTypes.GaugeChart, + isDemoExample: true + } + ] }, { title: "Scatter", @@ -414,31 +444,31 @@ let allDemoGroups = [ { options: scatterDemos.doubleLinearScatterOptions, data: scatterDemos.doubleLinearScatterData, - chartType: chartTypes.ScatterChart, + chartType: chartTypes.ScatterChart }, { options: scatterDemos.scatterTimeSeriesOptions, data: scatterDemos.scatterTimeSeriesData, - chartType: chartTypes.ScatterChart, + chartType: chartTypes.ScatterChart }, { options: scatterDemos.scatterDiscreteOptions, data: scatterDemos.scatterDiscreteData, - chartType: chartTypes.ScatterChart, + chartType: chartTypes.ScatterChart }, { options: scatterDemos.scatterEmptyStateOptions, data: scatterDemos.scatterEmptyStateData, chartType: chartTypes.ScatterChart, - isDemoExample: false, + isDemoExample: false }, { options: scatterDemos.scatterSkeletonOptions, data: scatterDemos.scatterSkeletonData, chartType: chartTypes.ScatterChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Step", @@ -448,26 +478,26 @@ let allDemoGroups = [ { options: stepDemos.stepOptions, data: stepDemos.stepData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: stepDemos.stepTimeSeriesOptions, data: stepDemos.stepTimeSeriesData, - chartType: chartTypes.LineChart, + chartType: chartTypes.LineChart }, { options: stepDemos.stepEmptyStateOptions, data: stepDemos.stepEmptyStateData, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: stepDemos.stepSkeletonOptions, data: stepDemos.stepSkeletonData, chartType: chartTypes.LineChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Time series axis", @@ -476,19 +506,19 @@ let allDemoGroups = [ options: timeSeriesAxisDemos.lineTimeSeries15secondsOptions, data: timeSeriesAxisDemos.lineTimeSeriesData15seconds, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: timeSeriesAxisDemos.lineTimeSeriesMinuteOptions, data: timeSeriesAxisDemos.lineTimeSeriesDataMinute, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: timeSeriesAxisDemos.lineTimeSeries30minutesOptions, data: timeSeriesAxisDemos.lineTimeSeriesData30minutes, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: @@ -496,7 +526,7 @@ let allDemoGroups = [ data: timeSeriesAxisDemos.lineTimeSeriesDataHourlyDefaultTicksFormats, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { options: @@ -504,19 +534,19 @@ let allDemoGroups = [ data: timeSeriesAxisDemos.lineTimeSeriesDataHourlyCustomTicksFormats, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataDaily, options: timeSeriesAxisDemos.lineTimeSeriesDailyOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataWeekly, options: timeSeriesAxisDemos.lineTimeSeriesWeeklyOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: @@ -524,46 +554,46 @@ let allDemoGroups = [ options: timeSeriesAxisDemos.lineTimeSeriesMonthlyDefaultLocaleOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataMonthlyCustomLocale, options: timeSeriesAxisDemos.lineTimeSeriesMonthlyCustomLocaleOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataQuarterly, options: timeSeriesAxisDemos.lineTimeSeriesQuarterlyOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataYearly, options: timeSeriesAxisDemos.lineTimeSeriesYearlyOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataSingleDatum, options: timeSeriesAxisDemos.lineTimeSeriesSingleDatumOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesNoExtendedDomainData, options: timeSeriesAxisDemos.lineTimeSeriesNoExtendedDomainOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: timeSeriesAxisDemos.lineTimeSeriesDataTwoIdenticalLabels, options: timeSeriesAxisDemos.lineTimeSeriesTwoIdenticalLabelsOptions, chartType: chartTypes.LineChart, - isDemoExample: false, + isDemoExample: false }, { data: @@ -571,9 +601,9 @@ let allDemoGroups = [ options: timeSeriesAxisDemos.lineTimeSeriesAllLabelsInPrimaryFormatOptions, chartType: chartTypes.LineChart, - isDemoExample: false, - }, - ], + isDemoExample: false + } + ] }, { title: "Radar", @@ -581,20 +611,20 @@ let allDemoGroups = [ { data: radarDemos.radarData, options: radarDemos.radarOptions, - chartType: chartTypes.RadarChart, + chartType: chartTypes.RadarChart }, { data: radarDemos.radarWithMissingDataData, options: radarDemos.radarWithMissingDataOptions, - chartType: chartTypes.RadarChart, + chartType: chartTypes.RadarChart }, { data: radarDemos.radarDenseData, options: radarDemos.radarDenseOptions, - chartType: chartTypes.RadarChart, - }, - ], - }, + chartType: chartTypes.RadarChart + } + ] + } ] as any; const formatTitleString = (str) => diff --git a/packages/core/src/chart.ts b/packages/core/src/chart.ts index 7daf4ea48b..692c3c377e 100644 --- a/packages/core/src/chart.ts +++ b/packages/core/src/chart.ts @@ -5,7 +5,7 @@ import { LayoutGrowth, LayoutDirection, LegendOrientations, - Events as ChartEvents, + Events as ChartEvents } from "./interfaces"; // Misc @@ -16,7 +16,7 @@ import { Legend, LayoutComponent, Tooltip, - Spacer, + Spacer } from "./components"; import { Tools } from "./tools"; @@ -28,7 +28,7 @@ export class Chart { services: any = { domUtils: DOMUtils, events: Events, - transitions: Transitions, + transitions: Transitions }; model: ChartModel = new ChartModel(this.services); @@ -119,8 +119,8 @@ export class Chart { components: [new Title(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; const legendComponent = { @@ -128,8 +128,8 @@ export class Chart { components: [new Legend(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; const graphFrameComponent = { @@ -137,8 +137,8 @@ export class Chart { components: graphFrameComponents, growth: { x: LayoutGrowth.STRETCH, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; const isLegendEnabled = @@ -176,8 +176,8 @@ export class Chart { components: [new Spacer(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; const fullFrameComponent = { @@ -188,18 +188,18 @@ export class Chart { this.services, [ ...(isLegendEnabled ? [legendComponent] : []), - legendSpacerComponent, - graphFrameComponent, + ...(isLegendEnabled ? [legendSpacerComponent] : []), + graphFrameComponent ], { - direction: fullFrameComponentDirection, + direction: fullFrameComponentDirection } - ), + ) ], growth: { x: LayoutGrowth.STRETCH, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; // Add chart title if it exists @@ -212,8 +212,8 @@ export class Chart { components: [new Spacer(this.model, this.services)], growth: { x: LayoutGrowth.PREFERRED, - y: LayoutGrowth.FIXED, - }, + y: LayoutGrowth.FIXED + } }; topLevelLayoutComponents.push(titleSpacerComponent); @@ -226,9 +226,9 @@ export class Chart { this.services, topLevelLayoutComponents, { - direction: LayoutDirection.COLUMN, + direction: LayoutDirection.COLUMN } - ), + ) ]; } } diff --git a/packages/core/src/charts/gauge.ts b/packages/core/src/charts/gauge.ts new file mode 100644 index 0000000000..7990b9d337 --- /dev/null +++ b/packages/core/src/charts/gauge.ts @@ -0,0 +1,38 @@ +// Internal Imports +import { Chart } from "../chart"; +import * as Configuration from "../configuration"; +import { ChartConfig, GaugeChartOptions } from "../interfaces/index"; +import { Tools } from "../tools"; + +// Components +import { + Gauge, + // the imports below are needed because of typescript bug (error TS4029) + TooltipPie +} from "../components/index"; + +export class GaugeChart extends Chart { + constructor(holder: Element, chartConfigs: ChartConfig) { + super(holder, chartConfigs); + + // Merge the default options for this chart + // With the user provided options + this.model.setOptions( + Tools.mergeDefaultChartOptions( + Configuration.options.gaugeChart, + chartConfigs.options + ) + ); + + // Initialize data, services, components etc. + this.init(holder, chartConfigs); + } + + getComponents() { + // Specify what to render inside the graph-frame + const graphFrameComponents = [new Gauge(this.model, this.services)]; + + const components: any[] = this.getChartComponents(graphFrameComponents); + return components; + } +} diff --git a/packages/core/src/charts/index.ts b/packages/core/src/charts/index.ts index e96619103d..4f3beacb06 100644 --- a/packages/core/src/charts/index.ts +++ b/packages/core/src/charts/index.ts @@ -9,3 +9,4 @@ export * from "./scatter"; export * from "./pie"; export * from "./donut"; export * from "./radar"; +export * from "./gauge"; diff --git a/packages/core/src/components/graphs/gauge.ts b/packages/core/src/components/graphs/gauge.ts new file mode 100644 index 0000000000..ae64bb4758 --- /dev/null +++ b/packages/core/src/components/graphs/gauge.ts @@ -0,0 +1,397 @@ +// Internal Imports +import { Component } from "../component"; +import { DOMUtils } from "../../services"; +import { + Roles, + Events, + GaugeTypes, + ArrowDirections +} from "../../interfaces"; +import { Tools } from "../../tools"; + +// D3 Imports +import { select } from "d3-selection"; +import { arc } from "d3-shape"; + +// arrow paths for delta +const ARROW_UP_PATH_STRING = "4,10 8,6 12,10"; +const ARROW_DOWN_PATH_STRING = "12,6 8,10 4,6"; + +export class Gauge extends Component { + type = "gauge"; + + // We need to store our arcs so that addEventListeners() can access them + arc: any; + backgroundArc: any; + + init() { + const eventsFragment = this.services.events; + } + + getValue(): number { + const data = this.model.getData(); + const value = data.find((d) => d.group === "value")?.value ?? null; + return value; + } + + getValueRatio(): number { + const value = Tools.clamp(this.getValue(), 0, 100); + return value / 100; + } + + getDelta(): number { + const data = this.model.getData(); + const delta = data.find((d) => d.group === "delta")?.value ?? null; + return delta; + } + + getArcRatio(): number { + const options = this.model.getOptions(); + const type = Tools.getProperty(options, "gauge", "type"); + const arcRatio = type === GaugeTypes.FULL ? 1 : 0.5; + return arcRatio; + } + + getArcSize(): number { + return this.getArcRatio() * Math.PI * 2; + } + + getStartAngle(): number { + const arcSize = this.getArcSize(); + if (arcSize === 2 * Math.PI) { + return 0; + } + return -arcSize / 2; + } + + // use provided arrow direction or default to using the delta + getArrow(delta): string { + const options = this.model.getOptions(); + const arrowDirection = Tools.getProperty( + options, + "gauge", + "deltaArrow", + "direction" + ); + + switch (arrowDirection) { + case ArrowDirections.UP: + return ARROW_UP_PATH_STRING; + case ArrowDirections.DOWN: + return ARROW_DOWN_PATH_STRING; + default: + return delta > 0 + ? ARROW_UP_PATH_STRING + : ARROW_DOWN_PATH_STRING; + } + } + + render(animate = true) { + const self = this; + const svg = this.getContainerSVG(); + const options = this.model.getOptions(); + const { groupMapsTo } = options.data; + + const value = this.getValue(); + const valueRatio = this.getValueRatio(); + const arcSize = this.getArcSize(); + + // angles for drawing the gauge + const startAngle = this.getStartAngle(); + const rotationAngle = valueRatio * arcSize; + const currentAngle = startAngle + rotationAngle; + const endAngle = startAngle + arcSize; + + // Compute the outer radius needed + const radius = this.computeRadius(); + const innerRadius = this.getInnerRadius(); + + // draw the container and arc + this.backgroundArc = arc() + .innerRadius(innerRadius) + .outerRadius(radius) + .startAngle(currentAngle) + .endAngle(endAngle); + + this.arc = arc() + .innerRadius(innerRadius) + .outerRadius(radius) + .startAngle(startAngle) + .endAngle(currentAngle); + + // draw the container + DOMUtils.appendOrSelect(svg, "path.arc-background") + .attr("d", this.backgroundArc) + .attr("role", Roles.GROUP); + + // Add data arc + const arcValue = svg.selectAll("path.arc-foreground").data([value]); + + arcValue + .enter() + .append("path") + .attr("class", "arc-foreground") + .merge(arcValue) + .attr("d", this.arc) + .attr("fill", (d) => self.model.getFillColor(d[groupMapsTo])) + // a11y + .attr("role", Roles.GRAPHICS_SYMBOL) + .attr("aria-roledescription", "value") + .attr("aria-label", (d) => d.value); + + // Position Arc + svg.attr("transform", `translate(${radius}, ${radius})`); + + // draw the value and delta to the center + this.drawValueNumber(); + this.drawDelta(); + + arcValue.exit().remove(); + + // Add event listeners + this.addEventListeners(); + } + + /** + * draws the value number associated with the Gauge component in the center + */ + drawValueNumber() { + const svg = this.getContainerSVG(); + const options = this.model.getOptions(); + + const arcType = Tools.getProperty(options, "gauge", "type"); + const value = this.getValue(); + const delta = this.getDelta(); + + // Sizing and positions relative to the radius + const radius = this.computeRadius(); + + const valueFontSize = Tools.getProperty( + options, + "gauge", + "valueFontSize" + ); + // if there is a delta, use the size to center the numbers, otherwise center the valueNumber + const deltaFontSize = Tools.getProperty( + options, + "gauge", + "deltaFontSize" + ); + + const numberSpacing = Tools.getProperty( + options, + "gauge", + "numberSpacing" + ); + + // circular gauge without delta should have valueNumber centered + let numbersYPosition = 0; + if (arcType === GaugeTypes.FULL && !delta) { + numbersYPosition = deltaFontSize(radius); + } else if (arcType === GaugeTypes.SEMI && delta) { + // semi circular gauge we want the numbers aligned to the chart container + numbersYPosition = -(deltaFontSize(radius) + numberSpacing); + } + + // Add the numbers at the center + const numbersGroup = DOMUtils.appendOrSelect( + svg, + "g.gauge-numbers" + ).attr("transform", `translate(0, ${numbersYPosition})`); + + // Add the big number + const valueNumberGroup = DOMUtils.appendOrSelect( + numbersGroup, + "g.gauge-value-number" + ).attr("transform", "translate(-10, 0)"); // Optical centering for the presence of the smaller % symbol + + const numberFormatter = Tools.getProperty( + options, + "gauge", + "numberFormatter" + ); + const valueNumber = valueNumberGroup + .selectAll("text.gauge-value-number") + .data([value]); + + valueNumber + .enter() + .append("text") + .attr("class", "gauge-value-number") + .merge(valueNumber) + .style("font-size", `${valueFontSize(radius)}px`) + .attr("text-anchor", "middle") + .text((d) => numberFormatter(d)); + + // add the percentage symbol beside the valueNumber + const { + width: valueNumberWidth + } = DOMUtils.getSVGElementSize( + DOMUtils.appendOrSelect(svg, "text.gauge-value-number"), + { useBBox: true } + ); + + DOMUtils.appendOrSelect(valueNumberGroup, "text.gauge-value-symbol") + .style("font-size", `${valueFontSize(radius) / 2}px`) + .attr("x", valueNumberWidth / 2) + .text("%"); + } + + /** + * adds the delta number for the gauge + */ + drawDelta() { + const self = this; + const svg = this.getContainerSVG(); + const options = this.model.getOptions(); + const delta = this.getDelta(); + + // Sizing and positions relative to the radius + const radius = this.computeRadius(); + const deltaFontSize = delta + ? Tools.getProperty(options, "gauge", "deltaFontSize") + : () => 0; + const numberFormatter = Tools.getProperty( + options, + "gauge", + "numberFormatter" + ); + + const arrowSize = Tools.getProperty(options, "gauge", "deltaArrow", "size"); + const numberSpacing = Tools.getProperty( + options, + "gauge", + "numberSpacing" + ); + + const numbersGroup = DOMUtils.appendOrSelect(svg, "g.gauge-numbers"); + + // Add the smaller number of the delta + const deltaGroup = DOMUtils.appendOrSelect( + numbersGroup, + "g.gauge-delta" + ).attr( + "transform", + `translate(0, ${deltaFontSize(radius) + numberSpacing})` + ); + + const deltaNumber = deltaGroup + .selectAll("text.gauge-delta-number") + .data(delta !== null ? [delta] : []); + + deltaNumber + .enter() + .append("text") + .merge(deltaNumber) + .attr("class", "gauge-delta-number") + .attr("text-anchor", "middle") + .style("font-size", `${deltaFontSize(radius)}px`) + .text((d) => `${numberFormatter(d)}%`); + + // Add the caret for the delta number + const { + width: deltaNumberWidth + } = DOMUtils.getSVGElementSize( + DOMUtils.appendOrSelect(svg, ".gauge-delta-number"), + { useBBox: true } + ); + + // check if delta arrow is disabled + const arrowEnabled = Tools.getProperty(options, "gauge", "deltaArrow", "enabled"); + const deltaArrow = deltaGroup + .selectAll("svg.gauge-delta-arrow") + .data(delta !== null && arrowEnabled ? [delta] : []); + + deltaArrow + .enter() + .append("svg") + .merge(deltaArrow) + .attr("class", "gauge-delta-arrow") + .attr("x", -arrowSize(radius) - deltaNumberWidth / 2) + .attr("y", -arrowSize(radius) / 2 - deltaFontSize(radius) * 0.35) + .attr("width", arrowSize(radius)) + .attr("height", arrowSize(radius)) + .attr("viewBox", "0 0 16 16"); + + // Needed to correctly size SVG in Firefox + DOMUtils.appendOrSelect(deltaArrow, "rect.gauge-delta-arrow-backdrop") + .attr("width", "16") + .attr("height", "16") + .attr("fill", "none"); + + // Draw the arrow with status + const status = Tools.getProperty(options, "gauge", "status"); + DOMUtils.appendOrSelect(deltaArrow, "polygon.gauge-delta-arrow") + .classed(`status--${status}`, status !== null) + .attr("fill", () => (status === null ? "currentColor" : null)) + .attr("points", self.getArrow(delta)); + + deltaArrow.exit().remove(); + deltaNumber.exit().remove(); + } + + getInnerRadius() { + // Compute the outer radius needed + const radius = this.computeRadius(); + const arcWidth = Tools.getProperty( + this.model.getOptions(), + "gauge", + "arcWidth" + ); + return radius - arcWidth; + } + + addEventListeners() { + const self = this; + this.parent + .selectAll("path.arc") + .on("mouseover", function (datum) { + // Dispatch mouse event + self.services.events.dispatchEvent(Events.Gauge.ARC_MOUSEOVER, { + element: select(this), + datum + }); + }) + .on("mousemove", function (datum) { + const hoveredElement = select(this); + + // Dispatch mouse event + self.services.events.dispatchEvent(Events.Gauge.ARC_MOUSEMOVE, { + element: hoveredElement, + datum + }); + }) + .on("click", function (datum) { + // Dispatch mouse event + self.services.events.dispatchEvent(Events.Gauge.ARC_CLICK, { + element: select(this), + datum + }); + }) + .on("mouseout", function (datum) { + const hoveredElement = select(this); + + // Dispatch mouse event + self.services.events.dispatchEvent(Events.Gauge.ARC_MOUSEOUT, { + element: hoveredElement, + datum + }); + }); + } + + // Helper functions + protected computeRadius() { + const options = this.model.getOptions(); + const arcType = Tools.getProperty(options, "gauge", "type"); + + const { width, height } = DOMUtils.getSVGElementSize(this.parent, { + useAttrs: true + }); + const radius = + arcType === GaugeTypes.SEMI + ? Math.min(width / 2, height) + : Math.min(width / 2, height / 2); + + return radius; + } +} diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index 7b19001072..289da0149e 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -21,6 +21,7 @@ export * from "./graphs/line"; export * from "./graphs/scatter"; export * from "./graphs/scatter-stacked"; export * from "./graphs/pie"; +export * from "./graphs/gauge"; export * from "./graphs/donut"; export * from "./graphs/skeleton"; diff --git a/packages/core/src/configuration.ts b/packages/core/src/configuration.ts index 63d127fd52..81007bb716 100644 --- a/packages/core/src/configuration.ts +++ b/packages/core/src/configuration.ts @@ -8,6 +8,7 @@ import { StackedBarChartOptions, AreaChartOptions, PieChartOptions, + GaugeChartOptions, DonutChartOptions, BubbleChartOptions, RadarChartOptions, @@ -21,6 +22,7 @@ import { LegendOptions, LegendPositions, StackedBarOptions, + GaugeTypes } from "./interfaces"; import enUSLocaleObject from "date-fns/locale/en-US/index"; @@ -40,16 +42,16 @@ export const legend: LegendOptions = { items: { status: { ACTIVE: 1, - DISABLED: 0, + DISABLED: 0 }, horizontalSpace: 12, verticalSpace: 24, - textYOffset: 8, + textYOffset: 8 }, checkbox: { radius: 6.5, - spaceAfter: 4, - }, + spaceAfter: 4 + } }; /** @@ -57,11 +59,11 @@ export const legend: LegendOptions = { */ export const grid: GridOptions = { x: { - numberOfTicks: 5, + numberOfTicks: 5 }, y: { - numberOfTicks: 5, - }, + numberOfTicks: 5 + } }; /** @@ -70,12 +72,12 @@ export const grid: GridOptions = { export const baseTooltip: TooltipOptions = { datapoint: { horizontalOffset: 10, - enabled: true, + enabled: true }, title: { verticalOffset: 0.75, - width: 0.4, - }, + width: 0.4 + } }; export const axisChartTooltip: AxisTooltipOptions = Tools.merge( @@ -84,8 +86,8 @@ export const axisChartTooltip: AxisTooltipOptions = Tools.merge( { gridline: { enabled: true, - threshold: 0.02, - }, + threshold: 0.02 + } } as AxisTooltipOptions ); @@ -94,11 +96,11 @@ export const barChartTooltip: BarTooltipOptions = Tools.merge( axisChartTooltip, { datapoint: { - verticalOffset: 4, + verticalOffset: 4 }, gridline: { - enabled: false, - }, + enabled: false + } } as BarTooltipOptions ); @@ -107,17 +109,17 @@ export const barChartTooltip: BarTooltipOptions = Tools.merge( // and by TwoDimensionalAxes. const axes: AxesOptions = { top: { - includeZero: true, + includeZero: true }, bottom: { - includeZero: true, + includeZero: true }, left: { - includeZero: true, + includeZero: true }, right: { - includeZero: true, - }, + includeZero: true + } }; export const timeScale: TimeScaleOptions = { @@ -133,8 +135,8 @@ export const timeScale: TimeScaleOptions = { weekly: { primary: "eee, MMM d", secondary: "eee" }, monthly: { primary: "MMM yyyy", secondary: "MMM" }, quarterly: { primary: "QQQ ''yy", secondary: "QQQ" }, - yearly: { primary: "yyyy", secondary: "yyyy" }, - }, + yearly: { primary: "yyyy", secondary: "yyyy" } + } }; /** @@ -147,15 +149,15 @@ const chart: BaseChartOptions = { tooltip: baseTooltip, legend, style: { - prefix: "cc", + prefix: "cc" }, data: { groupMapsTo: "group", - loading: false, + loading: false }, color: { - scale: null, - }, + scale: null + } }; /** @@ -165,7 +167,7 @@ const axisChart: AxisChartOptions = Tools.merge({}, chart, { axes, timeScale, grid, - tooltip: axisChartTooltip, + tooltip: axisChartTooltip } as AxisChartOptions); /** @@ -173,12 +175,12 @@ const axisChart: AxisChartOptions = Tools.merge({}, chart, { */ const baseBarChart: BarChartOptions = Tools.merge({}, axisChart, { bars: { - maxWidth: 16, + maxWidth: 16 }, timeScale: Tools.merge(timeScale, { - addSpaceOnEdges: 1, + addSpaceOnEdges: 1 } as TimeScaleOptions), - tooltip: barChartTooltip, + tooltip: barChartTooltip } as BarChartOptions); /** @@ -204,8 +206,8 @@ const groupedBarChart: BarChartOptions = Tools.merge( */ const stackedBarChart: StackedBarChartOptions = Tools.merge({}, baseBarChart, { bars: Tools.merge({}, baseBarChart.bars, { - dividerSize: 1.5, - } as StackedBarOptions), + dividerSize: 1.5 + } as StackedBarOptions) } as BarChartOptions); /** @@ -215,8 +217,8 @@ const lineChart: LineChartOptions = Tools.merge({}, axisChart, { points: { // default point radius to 3 radius: 3, - filled: false, - }, + filled: false + } } as LineChartOptions); /** @@ -224,8 +226,8 @@ const lineChart: LineChartOptions = Tools.merge({}, axisChart, { */ const areaChart: AreaChartOptions = Tools.merge({}, lineChart, { timeScale: Tools.merge(timeScale, { - addSpaceOnEdges: 0, - } as TimeScaleOptions), + addSpaceOnEdges: 0 + } as TimeScaleOptions) } as LineChartOptions); /** @@ -241,8 +243,8 @@ const scatterChart: ScatterChartOptions = Tools.merge({}, axisChart, { // default point radius to 4 radius: 4, fillOpacity: 0.3, - filled: true, - }, + filled: true + } } as ScatterChartOptions); /** @@ -258,11 +260,11 @@ const bubbleChart: BubbleChartOptions = Tools.merge({}, axisChart, { ); return [ (smallerChartDimension * 3) / 400, - (smallerChartDimension * 25) / 400, + (smallerChartDimension * 25) / 400 ]; }, - fillOpacity: 0.2, - }, + fillOpacity: 0.2 + } } as BubbleChartOptions); /** @@ -274,7 +276,7 @@ const pieChart: PieChartOptions = Tools.merge({}, chart, { innerRadius: 2, padAngle: 0.007, hoverArc: { - outerRadiusOffset: 3, + outerRadiusOffset: 3 }, xOffset: 30, yOffset: 20, @@ -284,14 +286,36 @@ const pieChart: PieChartOptions = Tools.merge({}, chart, { offsetX: 15, offsetY: 12, horizontalLineLength: 8, - textMargin: 2, + textMargin: 2 }, labels: { - formatter: null, - }, - }, + formatter: null + } + } } as PieChartOptions); +/** + * options specific to gauge charts + */ +const gaugeChart: GaugeChartOptions = Tools.merge({}, chart, { + legend: { + enabled: false + }, + gauge: { + type: GaugeTypes.SEMI, + arcWidth: 16, + deltaArrow: { + size: (radius) => radius / 8, + enabled: true + }, + status: null, + numberSpacing: 10, + deltaFontSize: (radius) => radius / 8, + valueFontSize: (radius) => radius / 2.5, + numberFormatter: (number) => (number.toFixed(2) % 1 !== 0) ? number.toFixed(2).toLocaleString() : number.toFixed().toLocaleString() + } +} as GaugeChartOptions); + /** * options specific to donut charts */ @@ -302,9 +326,9 @@ const donutChart: DonutChartOptions = Tools.merge({}, pieChart, { Math.min((radius / 100) * 24, 24) + "px", titleFontSize: (radius) => Math.min((radius / 100) * 15, 15) + "px", titleYPosition: (radius) => Math.min((radius / 80) * 20, 20), - numberFormatter: (number) => Math.floor(number).toLocaleString(), - }, - }, + numberFormatter: (number) => Math.floor(number).toLocaleString() + } + } } as DonutChartOptions); /** @@ -314,26 +338,26 @@ const radarChart: RadarChartOptions = Tools.merge({}, chart, { radar: { axes: { angle: "key", - value: "value", + value: "value" }, opacity: { unselected: 0.1, - selected: 0.3, + selected: 0.3 }, xLabelPadding: 10, yLabelPadding: 8, yTicksNumber: 4, minRange: 10, xAxisRectHeight: 50, - dotsRadius: 5, + dotsRadius: 5 }, tooltip: { gridline: { - enabled: true, + enabled: true }, valueFormatter: (value) => - value !== null && value !== undefined ? value : "N/A", - }, + value !== null && value !== undefined ? value : "N/A" + } } as RadarChartOptions); export const options = { @@ -350,6 +374,7 @@ export const options = { pieChart, donutChart, radarChart, + gaugeChart }; /** @@ -358,8 +383,8 @@ export const options = { export const lines = { opacity: { unselected: 0.3, - selected: 1, - }, + selected: 1 + } }; /** @@ -368,8 +393,8 @@ export const lines = { export const area = { opacity: { unselected: 0, - selected: 0.4, - }, + selected: 0.4 + } }; /** @@ -378,8 +403,8 @@ export const area = { export const areas = { opacity: { unselected: 0.3, - selected: 1, - }, + selected: 1 + } }; /** @@ -387,34 +412,34 @@ export const areas = { */ export const transitions = { default: { - duration: 300, + duration: 300 }, pie_slice_mouseover: { - duration: 100, + duration: 100 }, pie_chart_titles: { - duration: 375, + duration: 375 }, graph_element_mouseover_fill_update: { - duration: 100, + duration: 100 }, graph_element_mouseout_fill_update: { - duration: 100, - }, + duration: 100 + } }; export const axis = { ticks: { number: 7, - rotateIfSmallerThan: 30, + rotateIfSmallerThan: 30 }, - paddingRatio: 0.1, + paddingRatio: 0.1 }; export const spacers = { default: { - size: 24, - }, + size: 24 + } }; export const tickSpaceRatioVertical = 2.5; diff --git a/packages/core/src/interfaces/charts.ts b/packages/core/src/interfaces/charts.ts index 5f511dfec9..b884985b64 100644 --- a/packages/core/src/interfaces/charts.ts +++ b/packages/core/src/interfaces/charts.ts @@ -1,14 +1,19 @@ +import { + GaugeTypes, + Statuses, + ArrowDirections +} from "./enums"; import { LegendOptions, TooltipOptions, GridOptions, - AxesOptions, + AxesOptions } from "./index"; import { AxisTooltipOptions, BarTooltipOptions, BarOptions, - StackedBarOptions, + StackedBarOptions } from "./components"; import { TimeScaleOptions } from "./axis-scales"; @@ -178,10 +183,10 @@ export interface LineChartOptions extends ScatterChartOptions { * options for the curve of the line */ curve?: - | string - | { - name: string; - }; + | string + | { + name: string; + }; } /** @@ -239,6 +244,26 @@ export interface PieChartOptions extends BaseChartOptions { }; } +/** + * options specific to gauge charts + */ +export interface GaugeChartOptions extends PieChartOptions { + gauge?: { + arcWidth?: number; + deltaArrow?: { + direction?: ArrowDirections; + size?: Function; + enabled: Boolean; + } + status?: Statuses; + deltaFontSize?: Function; + numberSpacing?: number; + numberFormatter?: Function; + valueFontSize?: Function; + type?: GaugeTypes; + }; +} + /** * options specific to donut charts */ diff --git a/packages/core/src/interfaces/enums.ts b/packages/core/src/interfaces/enums.ts index b7aa65217c..420bffa9f8 100644 --- a/packages/core/src/interfaces/enums.ts +++ b/packages/core/src/interfaces/enums.ts @@ -8,7 +8,7 @@ export enum ChartTheme { DEFAULT = "default", G100 = "g100", G90 = "g90", - G10 = "g10", + G10 = "g10" } /** @@ -18,7 +18,7 @@ export enum AxisPositions { LEFT = "left", RIGHT = "right", TOP = "top", - BOTTOM = "bottom", + BOTTOM = "bottom" } /** @@ -29,7 +29,7 @@ export enum AxisPositions { */ export enum CartesianOrientations { VERTICAL = "vertical", - HORIZONTAL = "horizontal", + HORIZONTAL = "horizontal" } /** @@ -39,7 +39,7 @@ export enum ScaleTypes { TIME = "time", LINEAR = "linear", LOG = "log", - LABELS = "labels", + LABELS = "labels" } /** @@ -48,7 +48,7 @@ export enum ScaleTypes { export enum TooltipPosition { MOUSE = "mouse", TOP = "top", - BOTTOM = "bottom", + BOTTOM = "bottom" } /** @@ -57,7 +57,7 @@ export enum TooltipPosition { export enum TooltipTypes { DATAPOINT = "datapoint", GRIDLINE = "gridline", - TITLE = "title", + TITLE = "title" } /** @@ -67,7 +67,7 @@ export enum LegendPositions { RIGHT = "right", LEFT = "left", TOP = "top", - BOTTOM = "bottom", + BOTTOM = "bottom" } /** @@ -75,7 +75,7 @@ export enum LegendPositions { */ export enum LegendOrientations { HORIZONTAL = "horizontal", - VERTICAL = "vertical", + VERTICAL = "vertical" } /** @@ -85,7 +85,7 @@ export enum LayoutDirection { ROW = "row", COLUMN = "column", ROW_REVERSE = "row-reverse", - COLUMN_REVERSE = "column-reverse", + COLUMN_REVERSE = "column-reverse" } /** @@ -94,7 +94,7 @@ export enum LayoutDirection { export enum LayoutGrowth { FIXED = "fixed", PREFERRED = "preferred", - STRETCH = "stretch", + STRETCH = "stretch" } /** @@ -102,7 +102,7 @@ export enum LayoutGrowth { */ export enum CalloutDirections { LEFT = "left", - RIGHT = "right", + RIGHT = "right" } /** @@ -112,7 +112,7 @@ export enum Skeletons { GRID = "grid", VERT_OR_HORIZ = "vertOrHoriz", PIE = "pie", - DONUT = "donut", + DONUT = "donut" } /** @@ -121,7 +121,7 @@ export enum Skeletons { export enum TextAnchor { START = "start", MIDDLE = "middle", - END = "end", + END = "end" } /** @@ -130,5 +130,27 @@ export enum TextAnchor { export enum DominantBaseline { BASELINE = "baseline", MIDDLE = "middle", - HANGING = "hanging", + HANGING = "hanging" +} + +export enum GaugeTypes { + SEMI = "semi", + FULL = "full" +} + +/** + * enum of all possible callout directions + */ +export enum ArrowDirections { + UP = "up", + DOWN = "down" +} + +/** + * enum of carbon statuses + */ +export enum Statuses { + SUCCESS = "success", + WARNING = "warning", + DANGER = "danger" } diff --git a/packages/core/src/interfaces/events.ts b/packages/core/src/interfaces/events.ts index 2be28628d9..7cd59a478e 100644 --- a/packages/core/src/interfaces/events.ts +++ b/packages/core/src/interfaces/events.ts @@ -47,6 +47,16 @@ export enum Pie { SLICE_MOUSEOUT = "pie-slice-mouseout", } +/** + * enum of all gauge graph events + */ +export enum Gauge { + ARC_MOUSEOVER = "gauge-arc-mouseover", + ARC_MOUSEMOVE = "gauge-arc-mousemove", + ARC_CLICK = "gauge-arc-click", + ARC_MOUSEOUT = "gauge-arc-mouseout", +} + /** * enum of all bar graph events */ diff --git a/packages/core/src/styles/graphs/_gauge.scss b/packages/core/src/styles/graphs/_gauge.scss new file mode 100644 index 0000000000..a920e8230f --- /dev/null +++ b/packages/core/src/styles/graphs/_gauge.scss @@ -0,0 +1,19 @@ +.#{$prefix}--#{$charts-prefix}--gauge { + path.arc-background { + fill: $ui-01; + } + + .gauge-delta-arrow { + &.status--danger { + fill: $support-01; + } + + &.status--warning { + fill: $support-03; + } + + &.status--success { + fill: $support-02; + } + } +} diff --git a/packages/core/src/styles/graphs/index.scss b/packages/core/src/styles/graphs/index.scss index d3d721507f..07ccce5d7d 100644 --- a/packages/core/src/styles/graphs/index.scss +++ b/packages/core/src/styles/graphs/index.scss @@ -3,3 +3,4 @@ @import "./line"; @import "./scatter"; @import "./radar"; +@import "./gauge"; diff --git a/packages/core/stories/all.stories.ts b/packages/core/stories/all.stories.ts index 9dbb3e62fb..7e884c599c 100644 --- a/packages/core/stories/all.stories.ts +++ b/packages/core/stories/all.stories.ts @@ -151,10 +151,9 @@ if (process.env.NODE_ENV !== "production") { storyUtils.addRadioButtonEventListeners(container); const getNewRow = () => { - const row = document.createElement("div"); - row.className = "bx--row"; - - return row; + const newRow = document.createElement("div"); + newRow.setAttribute("class", "bx--row"); + return newRow; }; const grid = document.createElement("div"); diff --git a/packages/react/src/gauge-chart.js b/packages/react/src/gauge-chart.js new file mode 100644 index 0000000000..5d785ff48b --- /dev/null +++ b/packages/react/src/gauge-chart.js @@ -0,0 +1,25 @@ +import React from "react"; + +import { GaugeChart as GC } from "@carbon/charts"; +import BaseChart from "./base-chart"; + +export default class GaugeChart extends BaseChart { + componentDidMount() { + this.chart = new GC( + this.chartRef, + { + data: this.props.data, + options: this.props.options + } + ); + } + + render() { + return ( +
this.chartRef = chartRef} + className="chart-holder"> +
+ ); + } +} diff --git a/packages/react/src/index.js b/packages/react/src/index.js index d7ae580d8f..5cdcbebcb3 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -5,6 +5,7 @@ import SimpleBarChart from "./bar-chart-simple"; import StackedBarChart from "./bar-chart-stacked"; import BubbleChart from "./bubble-chart"; import DonutChart from "./donut-chart"; +import GaugeChart from "./gauge-chart"; import LineChart from "./line-chart"; import PieChart from "./pie-chart"; import ScatterChart from "./scatter-chart"; @@ -18,6 +19,7 @@ export { StackedBarChart, BubbleChart, DonutChart, + GaugeChart, LineChart, PieChart, ScatterChart, diff --git a/packages/vue/src/ccv-gauge-chart.vue b/packages/vue/src/ccv-gauge-chart.vue new file mode 100644 index 0000000000..005935c170 --- /dev/null +++ b/packages/vue/src/ccv-gauge-chart.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/vue/src/index.js b/packages/vue/src/index.js index e41007579e..c8836b8e98 100644 --- a/packages/vue/src/index.js +++ b/packages/vue/src/index.js @@ -5,6 +5,7 @@ import CcvGroupedBarChart from './ccv-grouped-bar-chart.vue'; import CcvStackedBarChart from './ccv-stacked-bar-chart.vue'; import CcvBubbleChart from './ccv-bubble-chart.vue'; import CcvDonutChart from './ccv-donut-chart.vue'; +import CcvGaugeChart from './ccv-gauge-chart.vue'; import CcvLineChart from './ccv-line-chart.vue'; import CcvPieChart from './ccv-pie-chart.vue'; import CcvScatterChart from './ccv-scatter-chart.vue'; @@ -18,6 +19,7 @@ const components = [ CcvStackedBarChart, CcvBubbleChart, CcvDonutChart, + CcvGaugeChart, CcvLineChart, CcvPieChart, CcvScatterChart, @@ -64,6 +66,7 @@ export { CcvStackedBarChart, CcvBubbleChart, CcvDonutChart, + CcvGaugeChart, CcvLineChart, CcvPieChart, CcvScatterChart,