DevExtreme React - SVG-Based Widgets Customization
This article describes ways to customize SVG-based widgets. For a list of SVG-based widgets, refer to HTML- and SVG-Based Widgets. A similar article on HTML-based widgets is also available.
Palettes
A palette is a set of colors that mix well with each other. Palettes are used to colorize the following widget elements:
- BarGauge: bars, bar labels
- Chart: series, series points, series point labels, legend items
- CircularGauge: subvalue indicators, range container
- Funnel: funnel items, funnel item labels
- LinearGauge: subvalue indicators, range container
- PieChart: series, series point labels, legend items
- PolarChart: series, series points, series point labels, legend items
- RangeSelector: series in the background chart
- Sankey: nodes and links, node labels
- TreeMap: tiles, tile groups
- VectorMap: areas, markers, legend items
DevExtreme supports predefined and custom palettes. The default palette is Material. This and other predefined palettes are demonstrated in the following demo:
Apply a Palette
Every widget that supports palettes has a palette option. It accepts the name of a predefined or registered custom palette or an array of colors. In most widgets, this option should be set on the first level of the configuration object:
jQuery
$(function() { $("#pieChartContainer").dxPieChart({ // ... palette: "Harmony Light" // ===== or custom colors ===== // palette: ['#60a69f', '#78b6d9', '#6682bb', '#a37182', '#eeba69'] }); });
Angular
<dx-pie-chart ... palette="Harmony Light"> <!-- or custom colors --> <!-- [palette]="['#60a69f', '#78b6d9', '#6682bb', '#a37182', '#eeba69']"> --> </dx-pie-chart>
import { DxPieChartModule } from "devextreme-angular"; // ... export class AppComponent { // ... } @NgModule({ imports: [ // ... DxPieChartModule ], // ... })
Vue
<template> <DxPieChart ... palette="Harmony Light"> <!-- or custom colors --> <!-- :palette="['#60a69f', '#78b6d9', '#6682bb', '#a37182', '#eeba69']"> --> </DxPieChart> </template> <script> import { DxPieChart } from 'devextreme-vue/pie-chart'; export default { components: { DxPieChart } } </script>
React
import React from 'react'; import { PieChart } from 'devextreme-react/pie-chart'; const customPalette = ['#60a69f', '#78b6d9', '#6682bb', '#a37182', '#eeba69']; class App extends React.Component { render() { return ( <PieChart ... palette="Harmony Light"> {/* ===== or custom colors ===== */} {/* palette={customPalette}> */} </PieChart> ); } } export default App;
In the CircularGauge and LinearGauge, the palette can be specified in the rangeContainer and subvalueIndicator objects.
jQuery
$(function() { $("#circularGaugeContainer").dxCircularGauge({ // ... subvalues: [25, 40, 68], subvalueIndicator: { palette: "Soft Pastel" }, rangeContainer: { ranges: [ { startValue: 0, endValue: 30 }, { startValue: 30, endValue: 70 }, { startValue: 70, endValue: 100 }, ], palette: "Violet" } }); });
Angular
<dx-circular-gauge ... [subvalues]="[25, 40, 68]"> <dxo-subvalue-indicator palette="Soft Pastel"> </dxo-subvalue-indicator> <dxo-range-container palette="Harmony Light"> <dxi-range [startValue]="0" [endValue]="30"></dxi-range> <dxi-range [startValue]="30" [endValue]="70"></dxi-range> <dxi-range [startValue]="70" [endValue]="100"></dxi-range> </dxo-range-container> </dx-circular-gauge>
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { DxCircularGaugeModule } from 'devextreme-angular'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, DxCircularGaugeModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Vue
<template> <DxCircularGauge ... :subvalues="[25, 40, 68]"> <DxSubvalueIndicator palette="Soft Pastel" /> <DxRangeContainer palette="Harmony Light"> <DxRange :start-value="0" :end-value="30" /> <DxRange :start-value="30" :end-value="70" /> <DxRange :start-value="70" :end-value="100" /> </DxRangeContainer> </DxCircularGauge> </template> <script> import { DxCircularGauge, DxRangeContainer, DxRange, DxSubvalueIndicator } from 'devextreme-vue/circular-gauge'; export default { components: { DxCircularGauge, DxRangeContainer, DxRange, DxSubvalueIndicator } } </script>
React
import React from 'react'; import { CircularGauge, RangeContainer, Range, SubvalueIndicator } from 'devextreme-react/circular-gauge'; const subvalues = [25, 40, 68]; class App extends React.Component { render() { return ( <CircularGauge subvalues={subvalues}> <SubvalueIndicator palette="Soft Pastel" /> <RangeContainer palette="Violet"> <Range startValue={0} endValue={30} /> <Range startValue={30} endValue={70} /> <Range startValue={70} endValue={100} /> </RangeContainer> </CircularGauge> ); } } export default App;
In the TreeMap, the palette is part of the colorizer. In the VectorMap, it should be set for a specific layer. In the RangeSelector, the palette is specified in the chart object.
jQuery
$(function() { $("#treeMapContainer").dxTreeMap({ // ... colorizer: { palette: "Violet" } }); $("#vectorMapContainer").dxVectorMap({ // ... layers: [{ dataSource: DevExpress.viz.map.sources.world, palette: "Violet", paletteSize: 7, customize: function(elements) { var paletteIndex = 0; $.each(elements, function(_, element) { element.applySettings({ paletteIndex: paletteIndex == 7 ? paletteIndex = 0 : paletteIndex++ }); }); } }] }); $("#rangeSelectorContainer").dxRangeSelector({ dataSource: [ { arg: "A", val1: 1, val2: 3 }, { arg: "B", val1: 5, val2: 5 }, { arg: "C", val1: 10, val2: 7 } ], chart: { palette: "Soft Pastel", commonSeriesSettings: { type: "bar", argumentField: "arg" }, series: [ { valueField: "val1" }, { valueField: "val2" } ] } }); });
Angular
<dx-tree-map ... > <dxo-colorizer palette="Harmony Light"></dxo-colorizer> </dx-tree-map> <dx-vector-map ... > <dxi-layer [dataSource]="worldMap" palette="Violet" [paletteSize]="7" [customize]="colorizeMap"> </dxi-layer> </dx-vector-map> <dx-range-selector [dataSource]="[ { arg: 'A', val1: 1, val2: 3 }, { arg: 'B', val1: 5, val2: 5 }, { arg: 'C', val1: 10, val2: 7 } ]"> <dxo-chart palette="Soft Pastel"> <dxo-common-series-settings type="bar" argumentField="arg"></dxo-common-series-settings> <dxi-series value-field="val1"></dxi-series> <dxi-series value-field="val2"></dxi-series> </dxo-chart> </dx-range-selector>
import { Component } from '@angular/core'; import * as mapsData from 'devextreme/dist/js/vectormap-data/world.js'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(service: Service) { this.colorizeMap = this.colorizeMap.bind(this); } worldMap: any = mapsData.world; colorizeMap(elements) { let paletteIndex = 0; elements.forEach((element) => { element.applySettings({ paletteIndex: paletteIndex == 7 ? paletteIndex = 0 : paletteIndex++ }); }); } }
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { DxTreeMapModule, DxVectorMapModule, DxRangeSelector } from 'devextreme-angular'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, DxTreeMapModule, DxVectorMapModule, DxRangeSelector ], providers: [ ], bootstrap: [AppComponent] }) export class AppModule { }
Vue
<template> <div> <DxTreeMap ... > <DxColorizer palette="Harmony Light" /> </DxTreeMap> <DxVectorMap ... > <DxLayer :data-source="worldMap" palette="Violet" :palette-size="7" :customize="colorizeMap" /> </DxVectorMap> <DxRangeSelector :data-source="[ { arg: 'A', val1: 1, val2: 3 }, { arg: 'B', val1: 5, val2: 5 }, { arg: 'C', val1: 10, val2: 7 } ]"> <DxChart palette="Soft Pastel"> <DxCommonSeriesSettings type="bar" argument-field="arg" /> <DxSeries value-field="val1" /> <DxSeries value-field="val2" /> <DxChart> </DxRangeSelector> </div> </template> <script> import { DxTreeMap, DxColorizer } from 'devextreme-vue/tree-map'; import { DxVectorMap, DxLayer } from 'devextreme-vue/vector-map'; import * as mapsData from 'devextreme/dist/js/vectormap-data/world.js'; import { DxRangeSelector, DxChart, DxCommonSeriesSettings, DxSeries } from 'devextreme-vue/range-selector'; export default { components: { DxTreeMap, DxColorizer, DxVectorMap, DxLayer, DxRangeSelector, DxChart, DxCommonSeriesSettings, DxSeries }, data() { return { worldMap: mapsData.world } }, methods: { colorizeMap(elements) { let paletteIndex = 0; elements.forEach((element) => { element.applySettings({ paletteIndex: paletteIndex == 7 ? paletteIndex = 0 : paletteIndex++ }); }); } } } </script>
React
import React from 'react'; import { TreeMap, Colorizer } from 'devextreme-react/tree-map'; import { VectorMap, Layer } from 'devextreme-react/vector-map'; import * as mapsData from 'devextreme/dist/js/vectormap-data/world.js'; import { RangeSelector, Chart, Series, CommonSeriesSettings } from 'devextreme-react/range-selector'; const worldMap = mapsData.world; const rangeSelectorData = [ { arg: 'A', val1: 1, val2: 3 }, { arg: 'B', val1: 5, val2: 5 }, { arg: 'C', val1: 10, val2: 7 } ]; class App extends React.Component { render() { return ( <div> <TreeMap ... > <Colorizer palette="Violet" /> </TreeMap> <VectorMap> <Layer dataSource={worldMap} paletteSize={7} customize={this.colorizeMap} palette="Violet" /> </VectorMap> <RangeSelector dataSource={rangeSelectorData}> <Chart palette="Soft Pastel"> <CommonSeriesSettings type="bar" argumentField="arg" /> <Series valueField="val1" /> <Series valueField="val2" /> </Chart> </RangeSelector> </div> ); } colorizeMap(elements) { let paletteIndex = 0; elements.forEach((element) => { element.applySettings({ paletteIndex: paletteIndex == 7 ? paletteIndex = 0 : paletteIndex++ }); }); } } export default App;
Override a Palette Color
Set a widget element's color option to override a palette color for this element:
jQuery
$(function() { $("#chartContainer").dxChart({ // ... series: [{ color: "red", // ... }, { /* ... */ }, { /* ... */ }] }); $("#circularGaugeContainer").dxCircularGauge({ // ... rangeContainer: { ranges: [{ color: "red" // ... }, { /* ... */ }, { /* ... */ }] } }); $("#vectorMapContainer").dxVectorMap({ // ... layers: [{ // ... customize: function(elements) { $.each(elements, function(_, element) { element.applySettings({ color: "red" }); }); } }, { /* ... */ }, { /* ... */ }] }); });
Angular
<dx-chart ... > <dxi-series color="red" ... ></dxi-series> </dx-chart> <dx-circular-gauge ... > <dxo-range-container> <!-- ... --> <dxi-range color="red" ... ></dxi-range> </dxo-range-container> </dx-circular-gauge> <dx-vector-map ... > <dxi-layer ... [customize]="colorizeMap"> </dxi-layer> </dx-vector-map>
import { DxChartModule, DxCircularGaugeModule, DxVectorMapModule } from "devextreme-angular"; import * as mapsData from "devextreme/dist/js/vectormap-data/world.js"; // ... export class AppComponent { worldMap: any = mapsData.world; constructor() { this.colorizeMap = this.colorizeMap.bind(this); } colorizeMap(elements) { elements.forEach((element) => { element.applySettings({ color: "red" }); }); } } @NgModule({ imports: [ // ... DxChartModule ], // ... })
Vue
<template> <div> <DxChart ... > <DxSeries color="red" ... /> </DxChart> <DxCircularGauge ... > <DxRangeContainer ... > <DxRange color="red" ... /> </DxRangeContainer> </DxCircularGauge> <DxVectorMap ... > <DxLayer ... :customize="colorizeMap" /> </DxVectorMap> </div> </template> <script> import { DxChart, DxSeries } from 'devextreme-vue/tree-map'; import { DxCircularGauge, DxRangeContainer, DxRange } from 'devextreme-vue/circular-gauge'; import { DxVectorMap, DxLayer } from 'devextreme-vue/vector-map'; import * as mapsData from 'devextreme/dist/js/vectormap-data/world.js'; export default { components: { DxChart, DxSeries, DxCircularGauge, DxRangeContainer, DxRange, DxVectorMap, DxLayer }, data() { return { worldMap: mapsData.world } }, methods: { colorizeMap(elements) { elements.forEach((element) => { element.applySettings({ color: 'red' }); }); } } } </script>
React
import React from 'react'; import { Chart, Series } from 'devextreme-react/chart'; import { CircularGauge, RangeContainer, Range } from 'devextreme-react/circular-gauge'; import { VectorMap, Layer } from 'devextreme-react/vector-map'; import * as mapsData from 'devextreme/dist/js/vectormap-data/world.js'; const worldMap = mapsData.world; class App extends React.Component { render() { return ( <div> <Chart ... > <Series color="red" ... /> </Chart> <CircularGauge ... > <RangeContainer ... > <Range color="red" ... /> </RangeContainer> </CircularGauge> <VectorMap> <Layer ... customize={this.colorizeMap} /> </VectorMap> <div> ); } colorizeMap(elements) { elements.forEach((element) => { element.applySettings({ color: 'red' }); }); } } export default App;
Implement a Custom Palette
The easiest way to implement a custom palette is to assign an array of colors to the palette option (see Apply a Palette). However, this approach is only useful for a single widget or several widgets of the same type.
In other cases, we recommend implementing a custom palette as an object of the following structure:
var myPalette = { // Applies in the BarGauge, Chart, Funnel, PieChart, PolarChart, Sankey, and TreeMap with a discrete colorizer simpleSet: ['#60a69f', '#78b6d9', '#6682bb', '#a37182', '#eeba69'], // Applies in the CircularGauge and LinearGauge indicatingSet: ['#90ba58', '#eeba69', '#a37182'], // Applies in the VectorMap and TreeMap with a gradient or range colorizer gradientSet: ['#78b6d9', '#eeba69'] };
The custom palette should be registered using the registerPalette(paletteName, palette) method:
DevExpress.viz.registerPalette("myCustomPalette", myPalette); // ===== or when using modules ===== import { registerPalette } from "devextreme/viz/palette"; registerPalette("myCustomPalette", myPalette);
To apply it, assign the name used in the registration to the widgets' palette options as shown in the Apply a Palette article.
Get a Registered Palette
Call the DevExpress.viz.getPalette(paletteName) method to get a registered predefined or custom palette. The method's description provides information about the structure of the returned object.
var palette = DevExpress.viz.getPalette("Material"); // ===== or when using modules ===== import { getPalette } from "devextreme/viz/palette"; let palette = getPalette("Material");
Themes
Unlike CSS themes, which are collections of CSS classes, SVG themes are widget configurations. However, all predefined CSS themes have SVG counterparts. This allows HTML- and SVG-based widgets to have a uniform appearance when they are displayed on the same page.
If you already use a predefined CSS theme on the page, a corresponding SVG theme is applied automatically. Otherwise, you need to apply the SVG theme.
You can also create custom SVG themes.
Apply a Theme
To apply an SVG theme to a single widget, assign the theme's name to the widget's theme option.
You can also pass the theme's name to the DevExpress.viz.currentTheme(theme) method to apply the theme to all SVG-based widgets on the page. If the widgets were created before the method call, use the DevExpress.viz.refreshTheme() method to refresh the styling settings.
DevExpress.viz.currentTheme("material.blue.light"); DevExpress.viz.refreshTheme(); // ===== or when using modules ===== import { currentTheme, refreshTheme } from "devextreme/viz/themes"; currentTheme("material.blue.light"); refreshTheme();
Create a Custom Theme
You can define several custom SVG themes and switch between them. The following code declares a custom theme called myTheme
:
var customTheme = { name: 'myTheme', barGauge: { /* BarGauge configuration */ }, bullet: { /* Bullet configuration */ }, chart: { /* Chart configuration */ }, funnel: { /* Funnel configuration */ }, gauge: { /* CircularGauge and LinearGauge configuration */ }, map: { /* VectorMap configuration */ }, pie: { /* PieChart configuration */ }, polar: { /* PolarChart configuration */ }, rangeSelector: { /* RangeSelector configuration */ }, sankey: { /* Sankey configuration */ }, sparkline: { /* Sparkline configuration */ }, treeMap: { /* TreeMap configuration */ } };
You should use the DevExpress.viz.registerTheme(customTheme, baseTheme) method to register the custom theme. Pass the name of a predefined theme as the baseTheme
argument. This theme complements the custom theme if specific options are absent in the latter. In the following code, Generic Light is used as the base theme:
DevExpress.viz.registerTheme(customTheme, "generic.light"); // ===== or when using modules ===== import { registerTheme } from "devextreme/viz/themes"; registerTheme(customTheme, "generic.light");
Next, use the theme's name (myTheme
) to apply the theme.
If you have technical questions, please create a support ticket in the DevExpress Support Center.