Data Charts support funnel type. To use the Funnel Chart, you can set the data chart type to GC.Spread.Sheets.DataCharts.DataChartType.funnel. There are two kinds of funnel charts. One kind of funnel chart uses category and value data binding, while another one uses detail and value data binding.
For example, you can insert a funnel data chart and configure the data chart by method setChartConfig.
Notes: before using the data chart, tables in data manager should be initialized.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import './styles.css';
import { AppFunc } from './app-func';
createRoot(document.getElementById('app')).render(<AppFunc />);
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { SpreadSheets } from '@mescius/spread-sheets-react';
import GC from '@mescius/spread-sheets';
import '@mescius/spread-sheets-print';
import '@mescius/spread-sheets-shapes';
import '@mescius/spread-sheets-charts';
import '@mescius/spread-sheets-reportsheet-addon';
import '@mescius/spread-sheets-datacharts-addon';
import '@mescius/spread-sheets-tablesheet';
import '@mescius/spread-sheets-ganttsheet';
import '@mescius/spread-sheets-formula-panel';
import '@mescius/spread-sheets-designer-resources-en';
import { Designer } from '@mescius/spread-sheets-designer-react';
import './styles.css';
import '$DEMOROOT$/spread/source/js/react_vue/license.js';
import { registerlic } from '$DEMOROOT$/spread/source/js/designer/react_vue/license.js';
registerlic(GC);
export function AppFunc() {
return (
<div class='demo'>
{top === window
? <Designer styleInfo={{ height: '100%' }} designerInitialized={initDesigner} />
: <React.Fragment>
<div id='spread-host'>
<SpreadSheets workbookInitialized={initSpread} />
</div>
<div id='panel-host' />
</React.Fragment>
}
</div>
);
}
async function initDesigner(designer) {
await initSpread(designer.getWorkbook());
designer.refresh();
}
async function initSpread(spread) {
await initDataManager(spread);
spread.suspendPaint();
spread.setSheetCount(2);
const sheet1 = spread.getSheet(0);
sheet1.name('Category + Value');
const dataChart11 = sheet1.dataCharts.add('chart11', 20, 20, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart11.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
category: { field: 'Stage' },
},
}],
config: {
header: {
title: 'Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart12 = sheet1.dataCharts.add('chart12', 490, 20, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart12.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
category: { field: 'Stage' },
},
config: {
funnel: {
orientation: GC.Spread.Sheets.DataCharts.Orientation.horizontal,
},
},
}],
config: {
header: {
title: 'Horizontal Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart13 = sheet1.dataCharts.add('chart13', 20, 340, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart13.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
category: { field: 'Stage' },
},
config: {
funnel: {
funnelType: GC.Spread.Sheets.DataCharts.FunnelType.bar,
},
},
}],
config: {
header: {
title: 'Bar Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart14 = sheet1.dataCharts.add('chart14', 490, 340, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart14.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
category: { field: 'Stage' },
},
config: {
funnel: {
funnelType: GC.Spread.Sheets.DataCharts.FunnelType.pyramid,
},
},
}],
config: {
header: {
title: 'Pyramid Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const sheet2 = spread.getSheet(1);
sheet2.name('Detail + Value');
const dataChart21 = sheet2.dataCharts.add('chart21', 20, 20, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart21.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
details: [{ field: 'Stage' }],
},
config: {
funnel: {
topWidth: 1,
bottomWidth: 0.2,
neckHeight: 0.3,
},
},
}],
config: {
header: {
title: 'Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart22 = sheet2.dataCharts.add('chart22', 490, 20, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart22.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
details: [{ field: 'Stage' }],
},
config: {
funnel: {
topWidth: 1,
bottomWidth: 0.2,
neckHeight: 0.3,
orientation: GC.Spread.Sheets.DataCharts.Orientation.horizontal,
},
},
}],
config: {
header: {
title: 'Horizontal Funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart23 = sheet2.dataCharts.add('chart23', 20, 340, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart23.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
details: [{ field: 'Stage' }],
},
config: {
funnel: {
topWidth: 1,
bottomWidth: 0.3,
neckHeight: 0,
},
},
}],
config: {
header: {
title: 'Trapezoid funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
const dataChart24 = sheet2.dataCharts.add('chart24', 490, 340, 450, 300, GC.Spread.Sheets.DataCharts.DataChartType.funnel);
dataChart24.setChartConfig({
tableName: 'Conversion',
plots: [{
type: GC.Spread.Sheets.DataCharts.DataChartType.funnel,
encodings: {
values: [{ field: 'Count', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }],
details: [{ field: 'Stage' }],
},
config: {
funnel: {
topWidth: 1,
bottomWidth: 0,
neckHeight: 0,
},
},
}],
config: {
header: {
title: 'Pyramid funnel Chart',
padding: {
left: 16,
right: 16,
top: 5,
bottom: 0,
},
textStyle: {
fontSize: 19,
fontStyle: GC.Spread.Sheets.DataCharts.FontStyle.italic,
alignment: GC.Spread.Sheets.DataCharts.HAlign.left,
},
},
plotAreas: [{
legends: [{
type: GC.Spread.Sheets.DataCharts.LegendType.color,
textStyle: {
color: 'rgba(51,51,51,1)',
fontFamily: 'Calibri',
fontSize: 12,
},
}],
}],
},
});
new GC.Spread.Sheets.DataCharts.DataChartConfigPanel('panel-host', spread);
spread.getActiveSheet().dataCharts.all()[0].isSelected(true);
spread.resumePaint();
}
async function initDataManager(spread) {
const records = [
['Website Visits', 10000],
['User Registration', 6900],
['Activation', 5000],
['First Purchase', 2200],
['Month 2 Retention', 1000],
];
const columns = ['Stage', 'Count'];
await spread.dataManager().addTable('Conversion', {
data: records.map(record => {
const item = {};
columns.forEach((column, index) => {
item[column] = record[index];
});
return item;
}),
}).fetch();
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/react/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/react/node_modules/@mescius/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.css">
<script src="$DEMOROOT$/en/react/node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('./src/app');
</script>
</head>
<body>
<div id="app" style="height: 100%;"></div>
</body>
</html>
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.demo {
width: 100%;
height: 100%;
overflow: hidden;
}
#spread-host {
position: absolute;
top: 0;
left: 0;
width: calc(100% - 380px);
height: 100%;
}
#panel-host {
position: absolute;
top: 0;
right: 0;
width: 380px;
height: 100%;
}
(function (global) {
System.config({
transpiler: 'plugin-babel',
babelOptions: {
es2015: true,
react: true
},
meta: {
'*.css': { loader: 'css' }
},
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
'@mescius/spread-sheets-designer-resources-en': 'npm:@mescius/spread-sheets-designer-resources-en/index.js',
'@mescius/spread-sheets-designer-react': 'npm:@mescius/spread-sheets-designer-react/index.js',
'@mescius/spread-sheets-designer': 'npm:@mescius/spread-sheets-designer/index.js',
'@mescius/spread-sheets-barcode': 'npm:@mescius/spread-sheets-barcode/index.js',
'@mescius/spread-sheets-charts': 'npm:@mescius/spread-sheets-charts/index.js',
'@mescius/spread-sheets-languagepackages': 'npm:@mescius/spread-sheets-languagepackages/index.js',
'@mescius/spread-sheets-print': 'npm:@mescius/spread-sheets-print/index.js',
'@mescius/spread-sheets-pdf': 'npm:@mescius/spread-sheets-pdf/index.js',
'@mescius/spread-sheets-shapes': 'npm:@mescius/spread-sheets-shapes/index.js',
'@mescius/spread-sheets-io': 'npm:@mescius/spread-sheets-io/index.js',
'@mescius/spread-sheets-reportsheet-addon': 'npm:@mescius/spread-sheets-reportsheet-addon/index.js',
'@mescius/spread-sheets-datacharts-addon': 'npm:@mescius/spread-sheets-datacharts-addon/index.js',
'@mescius/spread-sheets-tablesheet': 'npm:@mescius/spread-sheets-tablesheet/index.js',
'@mescius/spread-sheets-ganttsheet': 'npm:@mescius/spread-sheets-ganttsheet/index.js',
'@mescius/spread-sheets-formula-panel': 'npm:@mescius/spread-sheets-formula-panel/index.js',
'@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/index.js',
'@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js',
'@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js',
'react': 'npm:react/cjs/react.production.js',
'react-dom': 'npm:react-dom/cjs/react-dom.production.js',
'react-dom/client': 'npm:react-dom/cjs/react-dom-client.production.js',
'scheduler': 'npm:scheduler/cjs/scheduler.production.js',
'css': 'npm:systemjs-plugin-css/css.js',
'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js',
'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
src: {
defaultExtension: 'jsx'
},
"node_modules": {
defaultExtension: 'js'
},
}
});
})(this);