Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
97aa540
change target framework to 9.0
pkamphuis Feb 6, 2025
2143eb9
local stuff
pkamphuis Feb 6, 2025
c9ca9c2
Migrate net10
pkamphuis Dec 30, 2025
36fc07d
improve test baseline (#3)
pkamphuis Dec 30, 2025
f638a62
Improve code generation (#4)
pkamphuis Dec 30, 2025
2533c4d
Create editorconfig (#5)
pkamphuis Dec 30, 2025
fb43a15
clean up generator project (#6)
pkamphuis Dec 31, 2025
4ce179e
extend code generation
pkamphuis Dec 31, 2025
3907fa5
code reorganization
pkamphuis Jan 1, 2026
62705c9
feat(generator): Add ISeries/IDataZoom type safety and Detail.Width/H…
pkamphuis Jan 1, 2026
e4d6e09
docs: Add circular dependency analysis and solutions
pkamphuis Jan 1, 2026
9c527fd
docs: Document hand-tuned override pattern and correct generator work…
pkamphuis Jan 1, 2026
ad9045e
feat(generator): automate EnumOrFunctionType pattern and fix Type pro…
pkamphuis Jan 1, 2026
b5679f7
refactor(types): convert CellSize to record with implicit operators
pkamphuis Jan 1, 2026
712e366
feat: fix generator naming collision by singularizing array item type…
pkamphuis Jan 2, 2026
95d7675
Improve type safety for dimensions, text, and series data properties
pkamphuis Jan 2, 2026
d7db628
fixed ParallelAxis
pkamphuis Jan 2, 2026
b0d0b2c
fixed the TreemapSeriesData Children issue
pkamphuis Jan 2, 2026
766ce0c
fixed Geo LayoutCenter
pkamphuis Jan 2, 2026
cfdde93
Fix generator special cases for ParallelAxis, TreemapSeriesData.Child…
pkamphuis Jan 2, 2026
84705ca
Fix TreeSeries.Data to generate as object? instead of DataData
pkamphuis Jan 2, 2026
e4ba59d
Fix Dataset.transform to use SingleOrArrayType<IDatasetTransform> pat…
pkamphuis Jan 2, 2026
98df5b3
Fix RadarSeriesData.Value to accept array or single value as object?
pkamphuis Jan 2, 2026
372821e
Fix MagicType.Type to be string[]? instead of List<object>?
pkamphuis Jan 2, 2026
c74c111
Fix Label.Rotate to be NumberOrString? instead of double?
pkamphuis Jan 2, 2026
c60ca40
Fix PiecewiseVisualMap.Pieces to be List<VisualMapPiece>? instead of …
pkamphuis Jan 2, 2026
19b09be
Generate accessor properties for GraphSeries.Links and Categories in …
pkamphuis Jan 2, 2026
10da0f4
Fix SankeySeries.Links to be object? with accessor properties for Ext…
pkamphuis Jan 2, 2026
486c20f
Add implicit conversions from double[] and double to StringArray for …
pkamphuis Jan 2, 2026
3973c5f
fixed warnings
pkamphuis Jan 2, 2026
3a73681
switched to polymorphic serialization for series
pkamphuis Jan 2, 2026
060bcd1
automatically generate ISeries
pkamphuis Jan 2, 2026
e3fabd4
save all files as UTF-8 without BOM
pkamphuis Jan 3, 2026
537e7a2
fixed test
pkamphuis Jan 3, 2026
d9b1bfc
Fixed VisualMap serialization test
pkamphuis Jan 3, 2026
aa81cd4
feat: Add polymorphic serialization for IDataZoom
pkamphuis Jan 3, 2026
9d30a6a
update documentation and copilot instructions
pkamphuis Jan 3, 2026
f5af794
update Generator to include echarts version in generated header
pkamphuis Jan 3, 2026
cc365f9
update the generated code with the new header
pkamphuis Jan 3, 2026
874e998
update documention on the current status
pkamphuis Jan 3, 2026
632f98d
add generator code quality improvements to plan
pkamphuis Jan 3, 2026
8cff5c3
update test
pkamphuis Jan 3, 2026
a5b6dc4
Generate Series class as implementing ISeries
pkamphuis Jan 3, 2026
ddfbff9
remove generated partial classes
pkamphuis Jan 3, 2026
6e75a8d
regenerate the types
pkamphuis Jan 3, 2026
dc8f431
Changed VisualMap to polymorphic serialization
pkamphuis Jan 3, 2026
8efc2bc
remove large file
pkamphuis Jan 3, 2026
0dd671b
Generate TypePatternAnalysisReport after Generation
pkamphuis Jan 4, 2026
d4755bc
update dev npm packages Vizor.ECharts
pkamphuis Jan 4, 2026
454368f
Map Calendar.cellSize to CellSize type
pkamphuis Jan 11, 2026
fc9a2c7
fix several demo sample charts
pkamphuis Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
380 changes: 380 additions & 0 deletions .editorconfig

Large diffs are not rendered by default.

175 changes: 175 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Copilot Instructions for vizor-echarts

## Project Overview
- **Purpose**: Blazor wrapper around Apache ECharts; main library targets .NET 10.
- **Structure**: src/Vizor.ECharts (core), src/Vizor.ECharts.Demo (Blazor server demo), src/Vizor.ECharts.Samples (area-based samples), src/Vizor.ECharts.Tests (unit tests via MSTest), src/Vizor.ECharts.BindingGenerator (code generation utility).
- **Documentation**: Detailed technical documentation is in the [doc/](doc/) folder, covering generator architecture, testing strategy, circular dependency solutions, memory profiling, and implementation guides.

## External References
Official Apache ECharts documentation and resources:
- **Examples Gallery**: https://echarts.apache.org/examples/en/index.html - Official ECharts examples (translate JavaScript to C#)
- **Cheat Sheet**: https://echarts.apache.org/en/cheat-sheet.html - Quick reference for common patterns
- **Option Documentation**: https://echarts.apache.org/en/option.html - Complete API reference for chart options
- **Tutorial**: https://echarts.apache.org/en/tutorial.html - In-depth guides (e.g., Dataset usage)
- **Online Editor**: https://echarts.apache.org/examples/en/editor.html - Test ECharts code snippets before translating to C#

## Core Architecture & Interop Flow

### C# → JS Pipeline
[src/Vizor.ECharts/EChart.razor](src/Vizor.ECharts/EChart.razor) inherits from [src/Vizor.ECharts/EChartBase.cs](src/Vizor.ECharts/EChartBase.cs) and serializes `ChartOptions` to JSON, passing it via `IJSRuntime` interop to [src/Vizor.ECharts/Scripts/vizor-echarts.js](src/Vizor.ECharts/Scripts/vizor-echarts.js). JS functions called: `vizorECharts.initChart` (initial render), `updateChart` (options update), `attachClickEvent` (click handling).

### Chart Options Model
- `ChartOptions` is a partial shell ([src/Vizor.ECharts/ChartOptions.cs](src/Vizor.ECharts/ChartOptions.cs)).
- Actual option/series types in [src/Vizor.ECharts/Options](src/Vizor.ECharts/Options) and [src/Vizor.ECharts/Series](src/Vizor.ECharts/Series).
- **Most files auto-generated** from ECharts option.json; preserve `[JsonPropertyName]` attributes and property naming conventions.

### Series Typing & Polymorphic Serialization
- Each series implements `ISeries` interface (auto-generated in [src/Vizor.ECharts/Series/Generated/ISeries.cs](src/Vizor.ECharts/Series/Generated/ISeries.cs)).
- Uses **[.NET 10 polymorphic serialization](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism)**: `[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]` on ISeries interface, with `[JsonDerivedType]` attributes for each series type (e.g., `LineSeries`, `BarSeries`).
- **No explicit Type property** in series classes—discriminator added automatically by serializer during JSON output.
- Same pattern applies to `IDataZoom` ([src/Vizor.ECharts/Options/DataZoom/Generated/IDataZoom.cs](src/Vizor.ECharts/Options/DataZoom/Generated/IDataZoom.cs)) with `InsideDataZoom` and `SliderDataZoom`.
- Polymorphic series data handled by [src/Vizor.ECharts/Types/SeriesDataConverterFactory.cs](src/Vizor.ECharts/Types/SeriesDataConverterFactory.cs).
- `SeriesData<T>`, `SeriesData<T,U>`, and `SeriesData<T,U,V>` generics serialize correctly via factory pattern.

## Data Loading Patterns

### Server-side (C#)
Use the `DataLoader` callback parameter on `EChart` component—invoked during render to populate options asynchronously.

### Browser-side (JS fetch)
Use `ExternalDataSource` ([src/Vizor.ECharts/Types/ExternalDataSource.cs](src/Vizor.ECharts/Types/ExternalDataSource.cs)) to define fetch URLs, optional path extraction, and `afterLoad` JS functions. **Inside options objects, reference via `ExternalDataSourceRef`** (not `ExternalDataSource`). Cached fetches accessible via `window.vizorECharts.getDataSource(fetchId)`.

### JS Functions in Options
Wrap raw JavaScript as `JavascriptFunction` ([src/Vizor.ECharts/Types/JavascriptFunction.cs](src/Vizor.ECharts/Types/JavascriptFunction.cs)); converter outputs raw function text for JS `eval`.

## Multi-Chart Coordination
Use `ChartGroup` ([src/Vizor.ECharts/ChartGroup.cs](src/Vizor.ECharts/ChartGroup.cs)) to synchronize updates across multiple charts via `UpdateAsync`.

## JSON Serialization & Performance
- **Cached serializer options by default** (`CacheJsonSerializerOptions=true`); reuse across all charts for memory efficiency (see Meziantou post linked in EChartBase comments).
- camelCase JSON convention via shared serializer.
- Custom `JsonConverters` parameter: use consistently or disable caching.

## Diagnostic Report & Pattern Analysis

The generator produces **TypePatternAnalysisReport.md** showing type mapping coverage:

### How to Access
- **Location**: `src/Vizor.ECharts.BindingGenerator/` (same directory as `typeinfo.txt`)
- **Triggered**: Automatically generated when running either:
- `dotnet run -- option-binding --input <path/to/option.json> --output src/Vizor.ECharts`
- `dotnet run -- typeinfo --input <path/to/option.json>`

### Report Contents
```
# Type Pattern Analysis Report
Generated: 2026-01-04

## Summary
- Total properties analyzed: 37,348
- ✅ Fully supported: 37,330 (99.95%)
- ⚠️ Partially supported: 2 (0.01%)
- ❌ Unsupported: 16 (0.04%)
- 🔍 Requires investigation: 0 (0%)

## Unsupported Patterns
[Details on which properties fall back to object/string]
[Sorted by frequency and impact]
```

### Using Report for Phase 4 (Close Gaps)
- Review unsupported patterns to prioritize next custom types
- High-frequency patterns get implementation priority
- Report tracks progress across generator runs

## Build & Test Workflows

### .NET Build & Run
- **Build all**: `dotnet build src/Vizor.ECharts.Demo.sln`
- **Run demo**: `dotnet run --project src/Vizor.ECharts.Demo/Vizor.ECharts.Demo.csproj`
- **Run tests**: `dotnet test --project src/Vizor.ECharts.Tests/Vizor.ECharts.Tests.csproj` (MSTest framework)
- **Pack NuGet**: `dotnet pack -c Release` (Release required; fails in Debug via csproj target). Package includes README and wwwroot assets.

### JS Build & Development
In `src/Vizor.ECharts`:
- `npm install` → install echarts & gulp toolchain
- `gulp` (or `gulp buildJs`) → minify vizor-echarts.js to wwwroot/js
- `gulp buildJsBundle` → bundle echarts + echarts-stat + vizor-echarts
- `gulp clean` → remove output files
Bundled JS (`vizor-echarts-bundle.js`) ships with package for no-dependency deployments.

### Debug Logging
DEBUG builds auto-enable `vizorECharts.changeLogging(true)`, logging serialized options & fetches to console. Use browser DevTools to inspect actual data sent/received.

## Resource Lifecycle
- **`DisposeAsync`** on `EChartBase`: removes from groups, clears cached serializer, calls `vizorECharts.disposeChart` (drops cached external data).
- **`ClearAsync`**: reset chart without disposing.

## Code Generation & Option.json Upgrades

**Rarely rerun generator.** Only when upgrading ECharts major version:
1. Build echarts-doc repo (`npm run build` → generates option.json).
2. Delete `src/Vizor.ECharts/Options/Generated` and `src/Vizor.ECharts/Series/Generated` folders.
3. Run: `dotnet run --project src/Vizor.ECharts.BindingGenerator -- option-binding --input <path/to/option.json> --output src/Vizor.ECharts`
4. **Diagnostic Report Generated**: `TypePatternAnalysisReport.md` is automatically created in the BindingGenerator directory (same location as `typeinfo.txt`), showing type mapping coverage and unsupported patterns.
5. **Polymorphic interfaces auto-generated**: Generator creates `ISeries.cs` and `IDataZoom.cs` in their respective Generated folders with .NET 10 `[JsonPolymorphic]`/`[JsonDerivedType]` attributes. Discriminators extracted from type property defaults in option.json.
6. Fix any compile errors (generator isn't 100% perfect).
7. **Hand-tuned overrides remain unchanged**: Files like `Series/Sankey/SankeySeriesLevel.cs` (outside Generated folders) are manually maintained and will NOT be regenerated. These are intentional architectural customizations.
8. Verify hand-tuned files still compile against newly generated types. Update manually if needed.

**Option.json Locations**:
- **Current version (5.6.0)**: [src/Vizor.ECharts.BindingGenerator/echart-options/5.6.0/option.json](src/Vizor.ECharts.BindingGenerator/echart-options/5.6.0/option.json)
- **Future version (6.0.0)**: Will be added to `src/Vizor.ECharts.BindingGenerator/echart-options/6.0.0/option.json` when upgrading
- **Source**: Generated from https://github.com/apache/echarts-doc repository by running `npm run build`
- **Version tracking**: Generator automatically extracts version from input path (e.g., "5.6.0" from "echart-options/5.6.0/option.json") and includes it in generated file headers

See [src/Vizor.ECharts.BindingGenerator/Readme.md](src/Vizor.ECharts.BindingGenerator/Readme.md) for details on identifying and maintaining hand-tuned files.

## Hand-Tuned Overrides Pattern

Some generated types are intentionally customized and maintained outside the Generated folders:

### Structure
- **Auto-generated**: `Series/Generated/Sankey/SankeySeriesLevels.cs` (in Generated subfolder, has "AUTO GENERATED" header)
- **Hand-tuned**: `Series/Sankey/SankeySeriesLevel.cs` (outside Generated, manually maintained, typically partial class)

### How to Identify
- Files WITH `// AUTO GENERATED - DO NOT EDIT` header in `Options/Generated/` or `Series/Generated/` → regenerated automatically
- Files WITHOUT header, located outside Generated folders → hand-maintained overrides

### When Hand-Tuning is Necessary
- API improvements or naming changes (e.g., singular vs. plural for clarity)
- Custom type conversions or serialization logic
- Architectural customizations that differ from option.json

### Maintenance Requirement
- Hand-tuned files are **NOT automatically updated** by the generator
- Must manually update when underlying generated types change
- Verify compilation after running generator on new option.json
Global enum mappings in [src/Vizor.ECharts.BindingGenerator/Types/TypeCollection.cs](src/Vizor.ECharts.BindingGenerator/Types/TypeCollection.cs) `AddMappedEnumType` calls. When a JSON property name maps to multiple possible enums (e.g., `"type"` can be `LineType` or `LegendType` depending on parent object), specify parent type as context: `AddMappedEnumType(new(...), "lineStyle")`.

## Testing Patterns
- Snapshot tests in [src/Vizor.ECharts.Tests/Unit/Serialization](src/Vizor.ECharts.Tests/Unit/Serialization).
- Use `ChartOptionsBuilder` helpers and `SnapshotHelper.AssertJsonSnapshot` for serialization validation.
- Test custom converters in Converters/ subfolder.

## Key Conventions
- **One `ExternalDataSource` per chart** (avoid static instances).
- **Render mode** defaults to SVG; set `Renderer = ChartRenderer.Canvas` if needed.
- **Theme** optional; null renders with default theme.
- **Click events** attached via `vizorECharts.attachClickEvent` → calls `HandleChartClick` on component.
- **Interop contract**: JS expects JSON strings for options/maps/fetchOpts. Null options show loading spinner only.

## Additional Documentation
For detailed technical documentation, see files in the [doc/](doc/) folder:
- **Circular_Dependency_Solutions.md** - Generator ↔ Vizor.ECharts circular dependency discussion and future abstractions layer plan
- **Generator_Implementation_Guide.md** - Code examples for generator improvements
- **Generator_Improvement_Plan.md** - Detailed specification for type mapping improvements
- **GENERATOR_IMPROVEMENT_EXECUTIVE_SUMMARY.md** - High-level generator improvement overview
- **Manual_Implementation_Analysis.md** - Analysis of existing type patterns
- **Testing_Strategy.md** - Test infrastructure and patterns
- **Memory_Profiling.md** - Performance optimization guidance
- **Node_Build_Toolchain.md** - JS build process documentation
- **VizorECharts_JS_Interop.md** - JavaScript interop architecture

Feel free to propose additions if key workflows or patterns are missing.
63 changes: 63 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Build & Pack

on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
global-json-file: './global.json'

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Restore .NET dependencies
run: dotnet restore src/Vizor.ECharts.Demo.sln

- name: Build .NET
run: dotnet build src/Vizor.ECharts.Demo.sln --no-restore --configuration Debug

- name: Test .NET
run: dotnet test --project src/Vizor.ECharts.Tests/Vizor.ECharts.Tests.csproj --no-build --configuration Debug --report-trx --results-directory ./artifacts/test-results

- name: Install npm dependencies
run: npm install
working-directory: src/Vizor.ECharts

- name: Build JS bundle with Gulp
run: npx gulp
working-directory: src/Vizor.ECharts

- name: Pack NuGet (Release)
run: dotnet pack -c Release src/Vizor.ECharts/Vizor.ECharts.csproj

- name: Upload NuGet artifact
uses: actions/upload-artifact@v4
with:
name: nuget-package
path: src/Vizor.ECharts/bin/Release/**/*.nupkg

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: artifacts/test-results

- name: Upload JS artifacts
uses: actions/upload-artifact@v4
with:
name: js-bundle
path: src/Vizor.ECharts/wwwroot/js/*.js
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ x64/
node_modules/
.vs/
/src/Vizor.ECharts/package-lock.json
/src/Vizor.EChartsGen/
42 changes: 42 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "BindingGenerator: option-binding",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-generator",
"program": "${workspaceFolder}/src/Vizor.ECharts.BindingGenerator/bin/Debug/net10.0/Vizor.ECharts.BindingGenerator.dll",
"args": [
"option-binding",
"--input",
"${workspaceFolder}/src/Vizor.ECharts.BindingGenerator/echart-options/5.6.0/option.json",
"--output",
"${workspaceFolder}/src/Vizor.EChartsGen"
],
"cwd": "${workspaceFolder}/src/Vizor.ECharts.BindingGenerator",
"stopAtEntry": false,
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"name": "BindingGenerator: typeinfo",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-generator",
"program": "${workspaceFolder}/src/Vizor.ECharts.BindingGenerator/bin/Debug/net10.0/Vizor.ECharts.BindingGenerator.dll",
"args": [
"typeinfo",
"--input",
"echart-options/5.6.0/option.json"
],
"cwd": "${workspaceFolder}/src/Vizor.ECharts.BindingGenerator",
"stopAtEntry": false,
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
58 changes: 58 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build-generator",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--configuration",
"Debug",
"${workspaceFolder}/src/Vizor.ECharts.BindingGenerator/Vizor.ECharts.BindingGenerator.csproj"
],
"group": {
"kind": "build",
"isDefault": false
},
"problemMatcher": "$msCompile",
"presentation": {
"reveal": "silent",
"panel": "shared"
}
},
{
"label": "build-solution",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"${workspaceFolder}/src/Vizor.ECharts.Demo.sln"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": "$msCompile"
},
{
"label": "run-tests",
"command": "dotnet",
"type": "shell",
"args": [
"test",
"--project",
"${workspaceFolder}/src/Vizor.ECharts.Tests/Vizor.ECharts.Tests.csproj"
],
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": "$msCompile",
"presentation": {
"reveal": "always",
"panel": "shared"
}
}
]
}
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Deterministic>true</Deterministic>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
Loading