package view import ( "fmt" "io" "slices" "strings" "github.com/go-echarts/go-echarts/v2/charts" "github.com/go-echarts/go-echarts/v2/opts" ) type Chart interface { AddX(interface{}) AddY(string, []int) Render(io.Writer) error Overlap(Chart) } func NewChart(name string) Chart { switch name { case "line": return NewLine() case "bar": return NewBar() case "stack": return NewStack() case "pie": return NewPie() default: panic("bad chart name " + name) } } type Line struct { *charts.Line } func NewLine() Line { return Line{Line: charts.NewLine()} } func (line Line) AddX(v interface{}) { line.SetXAxis(v) } func (line Line) AddY(name string, v []int) { y := make([]opts.LineData, len(v)) for i := range y { y[i].Value = v[i] } line.AddSeries(name, y). SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{ Stack: "stackB", })) } func (line Line) Overlap(other Chart) { overlapper, ok := other.(charts.Overlaper) if !ok { panic(fmt.Sprintf("cannot overlap %T", other)) } line.Line.Overlap(overlapper) } type Bar struct { *charts.Bar } func NewBar() Bar { return Bar{Bar: charts.NewBar()} } func (bar Bar) AddX(v interface{}) { bar.SetXAxis(v) } func (bar Bar) AddY(name string, v []int) { y := make([]opts.BarData, len(v)) for i := range v { y[i].Value = v[i] } bar.AddSeries(name, y) } func (bar Bar) Overlap(other Chart) { overlapper, ok := other.(charts.Overlaper) if !ok { panic(fmt.Sprintf("cannot overlap %T", other)) } bar.Bar.Overlap(overlapper) } type Stack struct { Bar } func NewStack() Stack { bar := NewBar() bar.SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{Stack: "x"})) return Stack{Bar: bar} } func (stack Stack) AddY(name string, v []int) { y := make([]opts.BarData, len(v)) for i := range v { y[i].Value = v[i] } stack.AddSeries(name, y). SetSeriesOptions(charts.WithBarChartOpts(opts.BarChart{ Stack: "stackA", })) } type Pie struct { *charts.Pie series []opts.PieData } func NewPie() *Pie { return &Pie{Pie: charts.NewPie()} } func (pie *Pie) AddX(v interface{}) { } func (pie *Pie) Render(w io.Writer) error { slices.SortFunc(pie.series, func(a, b opts.PieData) int { return strings.Compare(a.Name, b.Name) }) commonPrefixLen := -1 for i := 0; i < len(pie.series[0].Name) && i < len(pie.series[len(pie.series)-1].Name); i++ { if pie.series[0].Name[i] != pie.series[len(pie.series)-1].Name[i] { break } commonPrefixLen = i } for i := range pie.series { pie.series[i].Name = pie.series[i].Name[commonPrefixLen+1:] } pie.AddSeries("", pie.series) pie.SetGlobalOptions(charts.WithLegendOpts(opts.Legend{ Show: false, })) return pie.Pie.Render(w) } func (pie *Pie) AddY(name string, v []int) { for _, v := range v { pie.series = append(pie.series, opts.PieData{ Name: fmt.Sprintf("%s ($%d)", name, v), Value: v, }) } } func (pie *Pie) Overlap(other Chart) { panic("nope") }