Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Tables](tables.md)
- [Theming Tables](theming-tables.md)
- [Tables Summaries](tables-summaries.md)
- [Adding Headers and Footers to a Worksheet](worksheet-headers-footers.md)
- [Inserting images into spreadsheets](inserting-pictures.md)
- [Adding Headers/Footers to a Worksheet](worksheet-headers-footers.md)
- [Inserting Images](inserting-pictures.md)
- [Inserting Charts](inserting-charts.md)
- [Streaming Export API](streaming.md)
171 changes: 171 additions & 0 deletions docs/inserting-charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
## Inserting charts

Add charts to a workbook: create data, create a chart, add it, position it. That's all—just practical usage.

### Supported types
`bar` (clustered column), `line`, `pie`, `scatter`

### Core steps
1. Create a workbook & worksheet
2. Add data rows
3. Create a chart (ranges or fallback arrays)
4. Call `wb.addChart(chart)`
5. Anchor it (e.g. `twoCellAnchor`)
6. Generate files

### Option summary (ChartOptions)
| Option | Purpose | Notes |
|--------|---------|-------|
| type | `bar` | `line` | `pie` | `scatter` | Defaults to `bar` |
| title | Chart title | Omit for none |
| xAxisTitle | X axis label | Ignored for pie |
| yAxisTitle | Y axis label | Ignored for pie |
| width / height | Size override | Defaults used if omitted |
| categoriesRange | Category labels range | Skip for scatter when using `xValuesRange` |
| series | Array of `{ name, valuesRange }` | 2+ series => legend |
| series[].xValuesRange | Scatter X values range | Only for scatter |
| sheetName + categories + values | Fallback single series | Arrays instead of ranges |


### Quick start (multi‑series bar chart)
```ts
const wb = createWorkbook();
const ws = wb.createWorksheet({ name: 'Sales' });
wb.addWorksheet(ws);

ws.addRow(['Month', 'Q1', 'Q2']);
ws.addRow(['Jan', 10, 15]);
ws.addRow(['Feb', 20, 25]);
ws.addRow(['Mar', 30, 35]);

const chart = new Chart({
type: 'bar',
title: 'Quarterly Sales',
xAxisTitle: 'Month',
yAxisTitle: 'Revenue',
series: [
{ name: 'Q1', valuesRange: 'Sales!$B$2:$B$4' },
{ name: 'Q2', valuesRange: 'Sales!$C$2:$C$4' },
],
categoriesRange: 'Sales!$A$2:$A$4',
});
wb.addChart(chart);

chart.createAnchor('twoCellAnchor', { from: { x: 4, y: 1 }, to: { x: 10, y: 16 } });
ws.addDrawings(drawings.addDrawing(chart)); // or add drawings first then the chart

await wb.generateFiles();
```

### Line chart (with axis titles)
```ts
const lineChart = new Chart({
type: 'line',
title: 'Revenue Trend',
xAxisTitle: 'Month',
yAxisTitle: 'Total',
series: [{ name: 'Q1', valuesRange: 'Sales!$B$2:$B$13' }],
categoriesRange: 'Sales!$A$2:$A$13',
});
wb.addChart(lineChart);
```

### Pie chart
```ts
const pie = new Chart({
type: 'pie',
title: 'Share by Region',
series: [{ name: '2025', valuesRange: 'Regions!$B$2:$B$6' }],
categoriesRange: 'Regions!$A$2:$A$6',
});
wb.addChart(pie);
```

### Scatter chart
Provide both X and Y value ranges (numeric): (less common, placed last)
```ts
const scatter = new Chart({
type: 'scatter',
title: 'Distance vs Speed',
xAxisTitle: 'Distance',
yAxisTitle: 'Speed',
series: [{
name: 'Run A',
xValuesRange: 'Runs!$A$2:$A$11',
valuesRange: 'Runs!$B$2:$B$11',
}],
});
wb.addChart(scatter);
```

### Fallback single series (arrays)
```ts
const autoChart = new Chart({
sheetName: 'AutoData',
categories: ['Jan', 'Feb', 'Mar'],
values: [10, 20, 30],
title: 'Auto Series',
});
wb.addChart(autoChart); // legend omitted (only one series)
```

## Resizing (width & height)
```ts
new Chart({
title: 'Wide Chart',
width: 6_000_000,
height: 2_000_000,
series: [{ name: 'Q1', valuesRange: 'Sales!$B$2:$B$4' }],
categoriesRange: 'Sales!$A$2:$A$4',
});
```

## Positioning
Use a two-cell anchor:
```ts
chart.createAnchor('twoCellAnchor', { from: { x: 4, y: 1 }, to: { x: 10, y: 16 } });
```
Values are column/row indices (0-based).

### Legend
The legend only appears when the chart has two or more series.

- 1 series: legend is omitted automatically.
- 2+ series: legend lists each `series.name`.

Notes:
- Pie: if you add multiple series you get multiple pies; the legend shows the series names.
- Fallback (arrays) path creates only one series, so no legend.

Example (legend will show 2 entries):
```ts
new Chart({
type: 'bar',
title: 'Year Comparison',
series: [
{ name: '2024', valuesRange: 'Sales!$B$2:$B$5' },
{ name: '2025', valuesRange: 'Sales!$C$2:$C$5' },
],
categoriesRange: 'Sales!$A$2:$A$5',
});
```

### Troubleshooting
| Problem | Cause | Fix |
|---------|-------|-----|
| Missing chart | Not added to workbook | Call `wb.addChart(chart)` |
| No legend | Only one series | Add a second series |
| Axis titles missing | Using pie chart | Pie charts have no axes |
| Wrong data | Typo in range string | Check sheet name & `$A$1` format |

### Minimal example
```ts
const simple = new Chart({
type: 'bar',
series: [{ name: 'Sales', valuesRange: 'Sales!$B$2:$B$4' }],
categoriesRange: 'Sales!$A$2:$A$4',
});
wb.addChart(simple);
```

That's it — build your workbook and open in Excel.
2 changes: 2 additions & 0 deletions packages/demo/src/app-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Example14 from './examples/example14.js';
import Example15 from './examples/example15.js';
import Example16 from './examples/example16.js';
import Example17 from './examples/example17.js';
import Example18 from './examples/example18.js';
import GettingStarted from './getting-started.js';

export const navbarRouting = [
Expand Down Expand Up @@ -48,6 +49,7 @@ export const exampleRouting = [
{ name: 'example15', view: '/src/examples/example15.html', viewModel: Example15, title: '15- Streaming Excel Export' },
{ name: 'example16', view: '/src/examples/example16.html', viewModel: Example16, title: '16- Streaming Features Demo' },
{ name: 'example17', view: '/src/examples/example17.html', viewModel: Example17, title: '17- Streaming Export with Images' },
{ name: 'example18', view: '/src/examples/example18.html', viewModel: Example18, title: '18- Chart Demo' },
],
},
];
63 changes: 63 additions & 0 deletions packages/demo/src/examples/example18.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<div class="example18">
<div class="row">
<div class="col-md-12 title-desc">
<h2 class="bd-title">
Example 18: Create Chart
<span class="float-end links">
Code <span class="fa fa-link"></span>
<span class="small">
<a
target="_blank"
href="https://github.com/ghiscoding/excel-builder-vanilla/blob/main/packages/demo/src/examples/example18.html"
>html</a
>
|
<a target="_blank" href="https://github.com/ghiscoding/excel-builder-vanilla/blob/main/packages/demo/src/examples/example18.ts"
>ts</a
>
</span>
</span>
</h2>
<div class="demo-subtitle">Create a simple bar chart and export as Excel file.</div>
</div>
</div>

<div>
<div class="mb-2">
<button id="export-chart" class="btn btn-success btn-sm">
<i class="fa fa-download"></i>
Excel Export with Chart
</button>
</div>
<div class="row">
<div class="table-container col-sm-8">
<table class="table">
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jan</td>
<td>120</td>
<td>180</td>
</tr>
<tr>
<td>Feb</td>
<td>150</td>
<td>160</td>
</tr>
<tr>
<td>Mar</td>
<td>170</td>
<td>200</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
94 changes: 94 additions & 0 deletions packages/demo/src/examples/example18.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { downloadExcelFile, Workbook, Chart, Drawings } from 'excel-builder-vanilla';

export default class Example18 {
exportBtnElm!: HTMLButtonElement;

mount() {
this.exportBtnElm = document.querySelector('#export-chart') as HTMLButtonElement;
this.exportBtnElm.addEventListener('click', this.startProcess.bind(this));
}

unmount() {
this.exportBtnElm.removeEventListener('click', this.startProcess.bind(this));
}

async startProcess() {
// Base data (will be duplicated into each chart sheet)
const months = ['Jan', 'Feb', 'Mar'];
const q1 = [120, 150, 170];
const q2 = [180, 160, 200];

const wb = new Workbook();

// Helper: create a sheet that includes its own data table & a chart of given type
const createChartSheetWithLocalData = (type: 'bar' | 'line' | 'pie' | 'scatter', sheetName: string) => {
const ws = wb.createWorksheet({ name: sheetName });
let categoriesRange: string | undefined;
let seriesDefs: { name: string; valuesRange: string; xValuesRange?: string }[] = [];

if (type === 'scatter') {
// Provide a richer numeric dataset for scatter (X,Y pairs) with 8 points
const xVals = [10, 20, 30, 40, 55, 65, 80, 95];
const yVals = [12, 18, 34, 33, 50, 58, 72, 90];
ws.setData([['X', 'Y'], ...xVals.map((x, i) => [x, yVals[i]])]);
wb.addWorksheet(ws);
const xRange = `${sheetName}!$A$2:$A$${xVals.length + 1}`;
const yRange = `${sheetName}!$B$2:$B$${yVals.length + 1}`;
seriesDefs = [{ name: 'Y vs X', valuesRange: yRange, xValuesRange: xRange }];
} else {
// Use month/Q1/Q2 table for non-scatter charts
ws.setData([['Month', 'Q1', 'Q2'], ...months.map((m, i) => [m, q1[i], q2[i]])]);
wb.addWorksheet(ws);
categoriesRange = `${sheetName}!$A$2:$A$${months.length + 1}`;
const q1Range = `${sheetName}!$B$2:$B$${months.length + 1}`;
const q2Range = `${sheetName}!$C$2:$C$${months.length + 1}`;
switch (type) {
case 'pie':
seriesDefs = [
{ name: 'Q1', valuesRange: q1Range },
{ name: 'Q2', valuesRange: q2Range },
];
break;
default:
seriesDefs = [
{ name: 'Q1', valuesRange: q1Range },
{ name: 'Q2', valuesRange: q2Range },
];
break;
}
}

const drawings = new Drawings();
const chart = new Chart({
type,
title: `${sheetName} (${type}) Chart`,
xAxisTitle: type === 'pie' ? undefined : type === 'scatter' ? 'X Values' : 'Month',
yAxisTitle: type === 'pie' ? undefined : type === 'scatter' ? 'Y Values' : 'Values',
width: 640 * 9525,
height: 400 * 9525,
sheetName,
categoriesRange,
series: seriesDefs,
});

const anchor = chart.createAnchor('twoCellAnchor', {
from: { x: 4, y: 6 }, // start at internal column index 4 (visually appears around Excel column E due to default widths)
to: { x: 15, y: 30 }, // end column chosen to preserve approximate chart width
});
chart.anchor = anchor;
drawings.addDrawing(chart);
ws.addDrawings(drawings);
wb.addDrawings(drawings);
wb.addChart(chart);
};

// Create one sheet per chart type with its own data
createChartSheetWithLocalData('bar', 'Bar');
createChartSheetWithLocalData('line', 'Line');
createChartSheetWithLocalData('pie', 'Pie');
createChartSheetWithLocalData('scatter', 'Scatter');

// Export workbook (chart will be included if supported)
downloadExcelFile(wb, 'Multiple-Charts.xlsx');
}
}
1 change: 0 additions & 1 deletion packages/demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ class Main {
}
if (foundRouter?.view) {
this.currentRouter = foundRouter;
// const html = await import(/*@vite-ignore*/ `${foundRouter.view}?raw`).default;
document.querySelector('.panel-wm-content')!.innerHTML = pageLayoutGlobs[foundRouter.view] as string;
const vm = new foundRouter.viewModel() as ViewModel;
this.currentModel = vm;
Expand Down
Loading