Skip to content

cnguy/gopherjs-frappe-charts

Repository files navigation

GopherJS bindings for frappe/charts

Go Report Card GoDoc

"Simple, responsive, modern SVG Charts with zero dependencies"

This library provides a type-safe, builder-based API for the simple and awesome frappe/charts.

Event listener with navigable bar chart:

Alt text

Various chart operations (append, remove, pop) with (mock "realtime") randomized data and actions:

Alt text

Contents

Installation

Built on go 1.9

go get -u github.com/cnguy/gopherjs-frappe-charts

Make sure you also have the necessary JS files.

Currently, this API supports frappe/[email protected] (maybe 0.0.8 too, since it seems that they didn't change the API in that version).

Dependencies will be managed via dep in the future.

Unfortunately, gopherjs is not vendorable at the moment.

Breaking Changes

This libary seeks to be updated to always work with the latest version of frappe charts. It also seeks to have a clean API so that users can easily bring in charts into their GopherJS-based website. One can also expect that this library will improve as I improve at Go.

Frappe hasn't changed their API dramatically so far, so one can expect that this library won't change dramatically as well. If there are breaking changes, it should be easy to update one's code by simply following the type errors.

The Bare Minimum

Following examples assume an HTML file like so:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>GopherJS bindings for frappe-charts</title>
</head>
<body>
	<div id="chart"></div>
	<div id="chart-2"></div>
	<div id="heatmap"></div>
	<script src="https://unpkg.com/[email protected]/dist/frappe-charts.min.iife.js"></script>
	<script src="static.js" data-cover></script>
</body>
</html>

where static.js is the name of your bundled JS file when your folder is named static and you run gopherjs build.

package main

import (
	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	chartData := charts.NewChartData()
	chartData.Labels = []string{
		"1", "2", "3",
	}
	chartData.Datasets = []*charts.Dataset{
		charts.NewDataset(
			"Some Data",
			[]float64{25, 40, 30},
		),
		charts.NewDataset(
			"Another Set",
			[]float64{25, 50, -10},
		),
	}

	_ = charts.NewBarChart("#chart", chartData).Render()
}

Alt text

This is the bare minimum needed to create a chart. Try swapping NewBarChart with other functions such as NewPercentageChart or NewPieChart.

frappe already sets defaults for a lot of properties. All New${Type}Chart calls handles the two necessary properties (the title and the chart data).

Everything else is optional (the title, colors, etc).

To learn how to provide more arguments, scroll down to Usage.

Usage

Simply declare chart data via NewChartData() and pass in array of *Datasets constructed by NewDataset().

This data can then be passed in to 99% of the charts (Heatmap is different).

This library uses the builder pattern to handle optional configurations. Every chart can be immediately rendered simply by doing

NewSomeChart("#parent", myData).Render()

If you would like to use optional configuration, check out the docs or simply just use your IDE (NewSomeChart returns a chainable object).

Tip: Prefer using the helpers instead of struct literals.

Hello World:

package main

import (
	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	// Prepare data
	chartData := charts.NewChartData()
	chartData.Labels = []string{
		"12am-3am", "3am-6pm", "6am-9am", "9am-12am",
		"12pm-3pm", "3pm-6pm", "6pm-9pm", "9am-12am",
	}
	chartData.Datasets = []*charts.Dataset{
		charts.NewDataset(
			"Some Data",
			[]float64{25, 40, 30, 35, 8, 52, 17, -4},
		),
		charts.NewDataset(
			"Another Set",
			[]float64{25, 50, -10, 15, 18, 32, 27, 14},
		),
	}

	chart := charts.NewBarChart("#chart", chartData).
		WithHeight(250). // `150` is default if this call is not applied.
		WithColors([]string{"light-blue", "violet"}).
		Render() // not calling Render returns a XXXChartArgs instance instead

	// TIP: Try swapping NewBarChart with NewPercentageChart or NewScatterChart
	// to see how easy it is to swap to a different type of chart.
	println(chart) // to suppress the annoying error
}

Output 1:

Alt text

Navigable:

package main

import (
	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	// Prepare data
	chartData := charts.NewChartData()
	chartData.Labels = []string{
		"2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017",
	}
	values := []float64{17, 40, 33, 44, 126, 156, 324, 333, 478, 495, 373}
	dataset := charts.NewDataset("Events", values)
	chartData.Datasets = []*charts.Dataset{dataset}

	chartTitle := "Fireball/Bolide Events - Yearly (more than 5 reports)"
	chart := charts.NewBarChart("#chart", chartData).
		WithTitle(chartTitle).
		WithColors([]string{"orange"}).
		SetIsNavigable(true). // ChartArgs#IsNavigable = 1
		SetIsSeries(true).    // ChartArgs#IsSeries = 1
		Render()

	println(chart)
}

Output 2:

Using left/right arrow keys to traverse the graph

Alt text

Line Properties:

package main

import (
	charts "github.com/cnguy/gopherjs-frappe-charts"
	chartsUtils "github.com/cnguy/gopherjs-frappe-charts/utils"
)

func main() {
	// Prepare data
	chartData := charts.NewChartData()
	chartData.Labels = chartsUtils.NumberLabelsToStr([]int{
		1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976,
		1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986,
		1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
		1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
		2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
	})
	values := []float64{
		132.9, 150.0, 149.4, 148.0, 94.4, 97.6, 54.1, 49.2, 22.5, 18.4,
		39.3, 131.0, 220.1, 218.9, 198.9, 162.4, 91.0, 60.5, 20.6, 14.8,
		33.9, 123.0, 211.1, 191.8, 203.3, 133.0, 76.1, 44.9, 25.1, 11.6,
		28.9, 88.3, 136.3, 173.9, 170.4, 163.6, 99.3, 65.3, 45.8, 24.7,
		12.6, 4.2, 4.8, 24.9, 80.8, 84.5, 94.0, 113.3, 69.8, 39.8,
	}
	dataset := charts.NewDataset("", values)
	chartData.Datasets = []*charts.Dataset{dataset}

	chartTitle := "Mean Total Sunspot Count - Yearly"
	chart := charts.NewLineChart("#chart", chartData).
		WithTitle(chartTitle).
		WithColors([]string{"blue"}).
		SetShowDots(false).
		SetHeatline(true).
		SetRegionFill(true).
		SetXAxisMode("tick").
		SetYAxisMode("span").
		SetIsSeries(true).
		Render()

	println(chart)
}

Output 3:

Alt text

Simple Aggregations:

package main

import (
	"time"

	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	chartData := charts.NewChartData()
	chartData.Labels = []string{
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
	}
	chartData.Datasets = []*charts.Dataset{
		charts.NewDataset("", []float64{25, 40, 30, 35, 8, 52, 17}),
		charts.NewDataset("", []float64{25, 50, -10, 15, 18, 32, 27}),
	}

	chart := charts.NewBarChart("#chart", chartData).
		WithColors([]string{"purple", "orange"}).
		Render()

	time.Sleep(1 * time.Second)
	chart.ShowSums()

	time.Sleep(1 * time.Second)
	chart.HideSums()

	time.Sleep(1 * time.Second)
	chart.ShowAverages()

	time.Sleep(1 * time.Second)
	chart.HideAverages()
}

Output 4:

Alt text

Event Listener:

package main

import (
	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	// Make bar chart
	reportCountList := []float64{17, 40, 33, 44, 126, 156,
		324, 333, 478, 495, 373}
	barChartData := charts.NewChartData()
	barChartData.Labels = []string{
		"2007", "2008", "2009", "2010", "2011", "2012",
		"2013", "2014", "2015", "2016", "2017",
	}
	barChartData.Datasets = []*charts.Dataset{
		charts.NewDataset("Events", reportCountList),
	}
	barChartTitle := "Fireball/Bolide Events - Yearly (more than 5 reports"
	barChart := charts.NewBarChart("#chart", barChartData).
		WithTitle(barChartTitle).
		WithHeight(180).
		WithColors([]string{"orange"}).
		SetIsNavigable(true).
		SetIsSeries(true).
		Render()

	// Make line chart
	lineChartValues := []float64{36, 46, 45, 32, 27, 31, 30, 36, 39, 49, 0, 0}
	lineChartData := charts.NewChartData()
	lineChartData.Labels = []string{
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
	}
	lineChartData.Datasets = []*charts.Dataset{
		charts.NewDataset("", lineChartValues),
	}
	lineChart := charts.NewLineChart("#chart-2", lineChartData).
		WithHeight(180).
		WithColors([]string{"green"}).
		SetIsSeries(true).
		Render()

	// Prepare update values for event listener
	moreLineData := []*charts.UpdateValueSet{
		charts.NewUpdateValueSet([]float64{4, 0, 3, 1, 1, 2, 1, 2, 1, 0, 1, 1}),
		charts.NewUpdateValueSet([]float64{2, 3, 3, 2, 1, 4, 0, 1, 2, 7, 11, 4}),
		charts.NewUpdateValueSet([]float64{7, 7, 2, 4, 0, 1, 5, 3, 1, 2, 0, 1}),
		charts.NewUpdateValueSet([]float64{0, 2, 6, 2, 2, 1, 2, 3, 6, 3, 7, 10}),
		charts.NewUpdateValueSet([]float64{9, 10, 8, 10, 6, 5, 8, 8, 24, 15, 10, 13}),
		charts.NewUpdateValueSet([]float64{9, 13, 16, 9, 4, 5, 7, 10, 14, 22, 23, 24}),
		charts.NewUpdateValueSet([]float64{20, 22, 28, 19, 28, 19, 14, 19, 51, 37, 29, 38}),
		charts.NewUpdateValueSet([]float64{29, 20, 22, 16, 16, 19, 24, 26, 57, 31, 46, 2}),
		charts.NewUpdateValueSet([]float64{36, 24, 38, 27, 15, 22, 24, 38, 32, 57, 139, 26}),
		charts.NewUpdateValueSet([]float64{37, 36, 32, 33, 12, 34, 52, 45, 58, 57, 64, 35}),
		charts.NewUpdateValueSet([]float64{36, 46, 45, 32, 27, 31, 30, 36, 39, 49, 0, 0}),
	}

	barChart.AddDataSelectListener(func(event *charts.DataSelectEvent) {
		updateValues := &charts.UpdateValuesArgs{
			Values: []*charts.UpdateValueSet{moreLineData[event.Index]},
			// keep labels same as before
			Labels: lineChartData.Labels,
		}
		lineChart.UpdateValues(updateValues)
	})
}

Output 5:

Alt text

Heatmaps

package main

import (
	"math/rand"
	"strconv"

	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	data := make(map[string]int)
	currentTimestamp := 1477699200
	for i := 0; i < 375; i++ {
		data[strconv.Itoa(currentTimestamp)] = rand.Intn(10)
		currentTimestamp += 86400
	}
	charts.NewHeatmapChart("#chart", data).
		WithHeight(115).
		/* Halloween colors */
		// WithLegendColors([]string{"#ebedf0", "#fdf436", "#ffc700", "#ff9100", "#06001c"}).
		Render()
}
With discrete domains set to default

Alt text

With discrete domains set to 1 via chartArgs.SetDiscreteDomain(true)

Alt text

With different colors

Alt text

Realtime

package main

import (
	"math/rand"
	"strconv"
	"time"

	charts "github.com/cnguy/gopherjs-frappe-charts"
)

func main() {
	chartData := charts.NewChartData()
	chartData.Labels = []string{
		"1", "2", "3",
	}
	chartData.Datasets = []*charts.Dataset{
		charts.NewDataset(
			"Some Data",
			[]float64{25, 40, 30},
		),
		charts.NewDataset(
			"Another Set",
			[]float64{25, 50, -10},
		),
	}

	chart := charts.NewScatterChart("#chart", chartData).
		WithHeight(250).
		WithColors([]string{"red", "green"}).
		Render()
	temp := 4
	for i := 0; i < 100; i++ {
		go func(i interface{}) {
			val := rand.Intn(3) + 2*i.(int)
			println(i, "sleeping for", val)
			time.Sleep(time.Duration(val) * time.Second)
			newLabel := strconv.Itoa(temp)
			actionCond := rand.Intn(4)
			switch actionCond {
			case 0:
				chart.RemoveDataPoint(rand.Int())
			case 1:
				chart.PopDataPoint()
			case 2:
				chart.AppendDataPoint([]float64{float64(rand.Intn(1000)), float64(rand.Intn(1000))}, newLabel)
			case 3:
				chart.AddDataPoint([]float64{float64(rand.Intn(200)), float64(rand.Intn(200))}, newLabel, rand.Intn(200))
			}
			temp++
			println(temp)
		}(i)
	}

	println("is this async?")
}

Output 6:

Alt text

Disclaimer

I'm still a big Go noob, and so I don't know the proper way to manage releases and dependencies yet. I'll try to figure it out soon!

Contributions

Any contributions are welcome. :)

Changelog

here when available

About

[experimental/outdated; frappe v0.07] GopherJS bindings for frappe/charts - simple Go charts for your frontend

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published