1
0
mirror of https://github.com/nikolaydubina/calendarheatmap.git synced 2024-12-11 21:22:12 +02:00

WEB: test

This commit is contained in:
Nikolay 2021-08-10 18:42:20 +08:00
parent 89f5770670
commit 6a18fe577e
39 changed files with 608 additions and 230 deletions

View File

@ -16,25 +16,29 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.14
go-version: ^1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
run: go get -v -t -d ./...
- name: Build
run: go build -v ./...
- name: Test that can build
run: go build
- name: Test
run: go test -v -coverprofile=coverage.txt -covermode=atomic ./...
run: go test -v -coverprofile=coverage.txt -covermode=atomic ./charts
- name: Codecov
uses: codecov/codecov-action@v1.0.10
- name: Build web
run: make build-web
- name: Deploy
uses: JamesIves/github-pages-deploy-action@4.1.4
with:
branch: gh-pages
folder: web

View File

@ -2,17 +2,30 @@ build:
go build
test:
go test ./...
go test ./charts
docs: build
cat charts/testdata/basic.json | ./calendarheatmap > docs/basic.png
CALENDAR_HEATMAP_ASSETS_PATH=assets cat charts/testdata/basic.json | ./calendarheatmap -colorscale=purple-blue-9.csv > docs/colorscale-1.png
CALENDAR_HEATMAP_ASSETS_PATH=assets cat charts/testdata/basic.json | ./calendarheatmap -colorscale=green-blue-9.csv > docs/colorscale-2.png
CALENDAR_HEATMAP_ASSETS_PATH=assets cat charts/testdata/basic.json | ./calendarheatmap -colorscale=yellow-green-9.csv > docs/colorscale-3.png
cat charts/testdata/basic.json | CALENDAR_HEATMAP_ASSETS_PATH=assets ./calendarheatmap -colorscale=purple-blue-9.csv > docs/colorscale-1.png
cat charts/testdata/basic.json | CALENDAR_HEATMAP_ASSETS_PATH=assets ./calendarheatmap -colorscale=green-blue-9.csv > docs/colorscale-2.png
cat charts/testdata/basic.json | CALENDAR_HEATMAP_ASSETS_PATH=assets ./calendarheatmap -colorscale=yellow-green-9.csv > docs/colorscale-3.png
cat charts/testdata/basic.json | ./calendarheatmap -locale=ko_KR > docs/korean.png
cat charts/testdata/basic.json | ./calendarheatmap -locale=ko_KR -output=svg > docs/korean.svg
cat charts/testdata/basic.json | ./calendarheatmap -labels=false > docs/nolabels.png
cat charts/testdata/basic.json | ./calendarheatmap -monthsep=false > docs/noseparator.png
cat charts/testdata/basic.json | ./calendarheatmap -labels=false -monthsep=false > docs/noseparator_nolabels.png
.PHONY: build test docs
build-web:
cp "$$(go env GOROOT)/misc/wasm/wasm_exec.js" web/
cp -r assets web/assets
cd web; GOARCH=wasm GOOS=js go build -o main.wasm main.go
run-web:
cd web; python3 -m http.server 8000
clean:
-rm web/wasm_exec.js
-rm web/main.wasm
-rm -rf web/assets
.PHONY: build test docs build-web run-web clean

View File

@ -36,20 +36,22 @@ const (
// HeatmapConfig contains config of calendar heatmap image
type HeatmapConfig struct {
Counts map[string]int
ColorScale ColorScale
DrawMonthSeparator bool
DrawLabels bool
BoxSize int
Margin int
TextWidthLeft int
TextHeightTop int
TextColor color.RGBA
BorderColor color.RGBA
Locale string
Format string
FontFace font.Face
ShowWeekdays map[time.Weekday]bool
Counts map[string]int
ColorScale ColorScale
DrawMonthSeparator bool
DrawLabels bool
BoxSize int
Margin int
MonthSeparatorWidth int
MonthLabelYOffset int
TextWidthLeft int
TextHeightTop int
TextColor color.RGBA
BorderColor color.RGBA
Locale string
Format string
FontFace font.Face
ShowWeekdays map[time.Weekday]bool
}
// WriteHeatmap writes image with heatmap and additional elements
@ -79,7 +81,7 @@ func WriteHeatmap(conf HeatmapConfig, w io.Writer) error {
MaxY: height - conf.Margin,
Margin: conf.Margin,
BoxSize: conf.BoxSize,
Width: 5,
Width: conf.MonthSeparatorWidth,
Color: conf.BorderColor,
},
)
@ -88,20 +90,18 @@ func WriteHeatmap(conf HeatmapConfig, w io.Writer) error {
labelsProvider := NewLabelsProvider(conf.Locale)
if conf.DrawLabels {
visitors = append(visitors, &MonthLabelsVisitor{FontFace: conf.FontFace, Img: img, YOffset: 50, Color: conf.TextColor, LabelsProvider: labelsProvider})
}
visitors = append(visitors, &MonthLabelsVisitor{
FontFace: conf.FontFace,
Img: img,
YOffset: conf.MonthLabelYOffset,
Color: conf.TextColor,
LabelsProvider: labelsProvider,
})
for iter := NewDayIterator(conf.Counts, offset, conf.BoxSize, conf.Margin); !iter.Done(); iter.Next() {
for _, v := range visitors {
v.Visit(iter)
}
}
if conf.DrawLabels {
drawWeekdayLabels(
conf.FontFace,
img,
offset,
image.Point{X: 0, Y: conf.TextHeightTop},
conf.ShowWeekdays,
conf.BoxSize,
conf.Margin,
@ -110,6 +110,12 @@ func WriteHeatmap(conf HeatmapConfig, w io.Writer) error {
)
}
for iter := NewDayIterator(conf.Counts, offset, conf.BoxSize, conf.Margin); !iter.Done(); iter.Next() {
for _, v := range visitors {
v.Visit(iter)
}
}
switch conf.Format {
case "png":
if err := png.Encode(w, img); err != nil {
@ -238,12 +244,10 @@ func (d *MonthLabelsVisitor) Visit(iter *DayIterator) {
// All weekday labels assumed to have same width, which really depends on font.
// offset argument is top right corner of where to insert column of weekday labels.
func drawWeekdayLabels(fontFace font.Face, img *image.RGBA, offset image.Point, weekdays map[time.Weekday]bool, boxSize int, margin int, color color.RGBA, lp LabelsProvider) {
width := 250
height := 100
y := offset.Y + height
y := offset.Y + boxSize - margin
for _, w := range weekdayOrder {
if weekdays[w] {
drawText(fontFace, img, image.Point{X: offset.X - width, Y: y}, lp.GetWeekday(w), color)
drawText(fontFace, img, image.Point{X: offset.X, Y: y}, lp.GetWeekday(w), color)
}
y += boxSize + margin
}

View File

@ -11,6 +11,7 @@ import (
"time"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"github.com/nikolaydubina/calendarheatmap/charts"
)
@ -28,7 +29,11 @@ func loadData(t *testing.T, filepath string) map[string]int {
}
func loadFontFace(t *testing.T, filepath string) font.Face {
fontFace, err := charts.LoadFontFaceFromFile(filepath)
fontFace, err := charts.LoadFontFaceFromFile(filepath, opentype.FaceOptions{
Size: 26,
DPI: 280,
Hinting: font.HintingNone,
})
if err != nil {
t.Error(err)
}
@ -55,20 +60,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-png-output.png"),
expectedPath: path.Join("testdata", "basic-png-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -76,20 +83,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-jpeg-output.jpeg"),
expectedPath: path.Join("testdata", "basic-jpeg-expected.jpeg"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "jpeg",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "jpeg",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -97,20 +106,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-svg-output.svg"),
expectedPath: path.Join("testdata", "basic-svg-expected.svg"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "svg",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "svg",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -118,20 +129,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-no-data-output.png"),
expectedPath: path.Join("testdata", "basic-no-data-expected.png"),
conf: charts.HeatmapConfig{
Counts: nil,
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: nil,
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -139,20 +152,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-no-labels-output.png"),
expectedPath: path.Join("testdata", "basic-no-labels-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: false,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: false,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -160,20 +175,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-no-separator-output.png"),
expectedPath: path.Join("testdata", "basic-no-separator-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: false,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: false,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -181,20 +198,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-korean-output.png"),
expectedPath: path.Join("testdata", "basic-korean-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "ko_KR",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "ko_KR",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{time.Monday: true, time.Wednesday: true, time.Friday: true},
},
},
{
@ -202,20 +221,22 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-no-weekdays-output.png"),
expectedPath: path.Join("testdata", "basic-no-weekdays-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: nil,
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: nil,
},
},
{
@ -223,19 +244,21 @@ func TestCharts(t *testing.T) {
outputPath: path.Join("testdata", "basic-all-weekdays-output.png"),
expectedPath: path.Join("testdata", "basic-all-weekdays-expected.png"),
conf: charts.HeatmapConfig{
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
Margin: 30,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
Counts: loadData(t, path.Join("testdata", "basic.json")),
ColorScale: loadColorscale(t, path.Join("..", "assets", "colorscales", "purple-blue-9.csv")),
DrawMonthSeparator: true,
DrawLabels: true,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
Margin: 30,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: loadFontFace(t, path.Join("..", "assets", "fonts", "Sunflower-Medium.ttf")),
ShowWeekdays: map[time.Weekday]bool{
time.Monday: true,
time.Tuesday: true,

View File

@ -1,6 +1,7 @@
package charts
import (
_ "embed"
"fmt"
"image"
"image/color"
@ -8,6 +9,9 @@ import (
"text/template"
)
//go:embed template.svg
var svgTemplate string
// Day is SVG template day parameters
type Day struct {
Count int
@ -44,7 +48,7 @@ func writeSVG(conf HeatmapConfig, w io.Writer) {
fullYearTemplate := template.Must(template.New("fullyear").Funcs(template.FuncMap{
"mul": func(a int, b int) int { return a * b },
"add": func(a int, b int) int { return a + b },
}).Parse(fullyear))
}).Parse(svgTemplate))
labelsProvider := NewLabelsProvider(conf.Locale)

View File

@ -1,7 +1,5 @@
package charts
const fullyear = `<svg width="752" height="112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(10, 20)">
<svg viewBox="0 0 762 112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(20, 20)">
{{range $w, $wo := $.Days}}<g transform="translate({{mul 14 $w}}, 0)">
{{range $d, $do := $wo}}{{if $do.Show}}<rect class="day" width="11" height="11" x="0" y="{{mul 13 $d}}" fill="{{$do.Color}}" data-count="{{$do.Count}}" data-date="{{$do.Date}}"></rect>{{end}}
{{end}}
@ -11,7 +9,6 @@ const fullyear = `<svg width="752" height="112" xmlns="http://www.w3.org/2000/sv
{{range $i, $label := $.LabelsMonths}}<text x="{{mul 14 $label.XOffset}}" y="-7" font-size="8px" fill="{{$.LabelsColor}}">{{$label.Label}}</text>
{{end}}
{{range $i, $o := $.LabelsWeekdays}}<text text-anchor="start" font-size="8px" dx="-10" dy="{{add 8 (mul 13 $i)}}" fill="{{$.LabelsColor}}" {{if not $o.Show}}style="display: none;"{{end}}>{{$o.Label}}</text>
{{range $i, $o := $.LabelsWeekdays}}<text text-anchor="start" font-size="8px" dx="-20" dy="{{add 8 (mul 13 $i)}}" fill="{{$.LabelsColor}}" {{if not $o.Show}}style="display: none;"{{end}}>{{$o.Label}}</text>
{{end}}
</g></svg>
`
</g></svg>

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -1,5 +1,5 @@
<svg width="752" height="112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(10, 20)">
<svg viewBox="0 0 762 112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(20, 20)">
<g transform="translate(0, 0)">
@ -546,12 +546,12 @@
<text x="686" y="-7" font-size="8px" fill="rgb(100,100,100)">Dec</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="8" fill="rgb(100,100,100)" >Mon</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="21" fill="rgb(100,100,100)" style="display: none;">Tue</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="34" fill="rgb(100,100,100)" >Wed</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="47" fill="rgb(100,100,100)" style="display: none;">Thu</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="60" fill="rgb(100,100,100)" >Fri</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="73" fill="rgb(100,100,100)" style="display: none;">Sat</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="86" fill="rgb(100,100,100)" style="display: none;">Sun</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="8" fill="rgb(100,100,100)" >Mon</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="21" fill="rgb(100,100,100)" style="display: none;">Tue</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="34" fill="rgb(100,100,100)" >Wed</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="47" fill="rgb(100,100,100)" style="display: none;">Thu</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="60" fill="rgb(100,100,100)" >Fri</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="73" fill="rgb(100,100,100)" style="display: none;">Sat</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="86" fill="rgb(100,100,100)" style="display: none;">Sun</text>
</g></svg>
</g></svg>

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -1,5 +1,5 @@
<svg width="752" height="112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(10, 20)">
<svg viewBox="0 0 762 112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(20, 20)">
<g transform="translate(0, 0)">
@ -546,12 +546,12 @@
<text x="686" y="-7" font-size="8px" fill="rgb(100,100,100)">Dec</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="8" fill="rgb(100,100,100)" >Mon</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="21" fill="rgb(100,100,100)" style="display: none;">Tue</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="34" fill="rgb(100,100,100)" >Wed</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="47" fill="rgb(100,100,100)" style="display: none;">Thu</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="60" fill="rgb(100,100,100)" >Fri</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="73" fill="rgb(100,100,100)" style="display: none;">Sat</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="86" fill="rgb(100,100,100)" style="display: none;">Sun</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="8" fill="rgb(100,100,100)" >Mon</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="21" fill="rgb(100,100,100)" style="display: none;">Tue</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="34" fill="rgb(100,100,100)" >Wed</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="47" fill="rgb(100,100,100)" style="display: none;">Thu</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="60" fill="rgb(100,100,100)" >Fri</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="73" fill="rgb(100,100,100)" style="display: none;">Sat</text>
<text text-anchor="start" font-size="8px" dx="-20" dy="86" fill="rgb(100,100,100)" style="display: none;">Sun</text>
</g></svg>
</g></svg>

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -12,16 +12,12 @@ import (
)
// LoadFontFace loads font face from bytes
func LoadFontFace(fontBytes []byte) (font.Face, error) {
func LoadFontFace(fontBytes []byte, options opentype.FaceOptions) (font.Face, error) {
f, err := opentype.Parse(fontBytes)
if err != nil {
return nil, fmt.Errorf("can not parse font file: %w", err)
}
face, err := opentype.NewFace(f, &opentype.FaceOptions{
Size: 26,
DPI: 280,
Hinting: font.HintingNone,
})
face, err := opentype.NewFace(f, &options)
if err != nil {
return nil, fmt.Errorf("can not create font face: %w", err)
}
@ -29,12 +25,12 @@ func LoadFontFace(fontBytes []byte) (font.Face, error) {
}
// LoadFontFaceFromFile loads font face from file
func LoadFontFaceFromFile(fontPath string) (font.Face, error) {
func LoadFontFaceFromFile(fontPath string, options opentype.FaceOptions) (font.Face, error) {
fontBytes, err := ioutil.ReadFile(fontPath)
if err != nil {
return nil, fmt.Errorf("can not open font file with error: %w", err)
}
return LoadFontFace(fontBytes)
return LoadFontFace(fontBytes, options)
}
// drawText inserts text into provided image at bottom left coordinate

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1,5 +1,5 @@
<svg width="752" height="112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(10, 20)">
<svg viewBox="0 0 762 112" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<g transform="translate(20, 20)">
<g transform="translate(0, 0)">
@ -546,12 +546,12 @@
<text x="686" y="-7" font-size="8px" fill="rgb(100,100,100)">12월</text>
<text text-anchor="start" font-size="8px" dx="-10" dy="8" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="21" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="34" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="47" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="60" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="73" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-10" dy="86" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="8" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="21" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="34" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="47" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="60" fill="rgb(100,100,100)" ></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="73" fill="rgb(100,100,100)" style="display: none;"></text>
<text text-anchor="start" font-size="8px" dx="-20" dy="86" fill="rgb(100,100,100)" style="display: none;"></text>
</g></svg>
</g></svg>

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

36
main.go
View File

@ -14,6 +14,8 @@ import (
_ "embed"
"github.com/nikolaydubina/calendarheatmap/charts"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
)
//go:embed assets/fonts/Sunflower-Medium.ttf
@ -56,7 +58,11 @@ func main() {
}
}
fontFace, err := charts.LoadFontFace(defaultFontFaceBytes)
fontFace, err := charts.LoadFontFace(defaultFontFaceBytes, opentype.FaceOptions{
Size: 26,
DPI: 280,
Hinting: font.HintingNone,
})
if err != nil {
log.Fatal(err)
}
@ -72,19 +78,21 @@ func main() {
}
conf := charts.HeatmapConfig{
Counts: counts,
ColorScale: colorscale,
DrawMonthSeparator: monthSep,
DrawLabels: labels,
Margin: 30,
BoxSize: 150,
TextWidthLeft: 350,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: locale,
Format: outputFormat,
FontFace: fontFace,
Counts: counts,
ColorScale: colorscale,
DrawMonthSeparator: monthSep,
DrawLabels: labels,
Margin: 30,
BoxSize: 150,
MonthSeparatorWidth: 5,
MonthLabelYOffset: 50,
TextWidthLeft: 300,
TextHeightTop: 200,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: locale,
Format: outputFormat,
FontFace: fontFace,
ShowWeekdays: map[time.Weekday]bool{
time.Monday: true,
time.Wednesday: true,

3
web/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
wasm_exec.js
main.wasm
assets/

1
web/CNAME Normal file
View File

@ -0,0 +1 @@
calendarheatmap.io

143
web/index.html Normal file
View File

@ -0,0 +1,143 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Bungee+Shade">
<style>
#banner {
font-family: 'Bungee Shade', serif;
font-size: 64px;
}
</style>
<script async defer src="https://buttons.github.io/buttons.js"></script>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-D6NR6EJGD7"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-D6NR6EJGD7');
</script>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<div class="container">
<div class="mx-auto" style="width: 700px;">
<div class="d-grid gap-4">
<div class="mb-3">
<div style="padding-top: 25px;">
<p id="banner" class="text-center">
CALENDAR HEATMAP
</p>
<div class="d-flex justify-content-center">
<a class="github-button" href="https://github.com/nikolaydubina/calendarheatmap" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star nikolaydubina/calendarheatmap on GitHub">Star</a>
</div>
</div>
</div>
<div class="mb-3">
<div id="output-container" style="height: 100px;"></div>
</div>
<form id="inputConfig">
<div class="d-grid gap-1">
<div class="mb-3">
<input type="radio" class="btn-check" name="formatOption" id="formatSVG" autocomplete="off">
<label class="btn btn-outline-secondary" for="formatSVG">SVG</label>
<input type="radio" class="btn-check" name="formatOption" id="formatPNG" autocomplete="off" checked>
<label class="btn btn-outline-secondary" for="formatPNG">PNG</label>
<input type="radio" class="btn-check" name="formatOption" id="formatJPEG" autocomplete="off">
<label class="btn btn-outline-secondary" for="formatJPEG">JPEG</label>
</div>
<div class="mb-3">
<textarea class="form-control" id="inputData" rows="10" style="resize: vertical; width: 100%; height: auto;">
{
"2020-05-16": 8,
"2020-05-17": 13,
"2020-05-18": 5,
"2020-05-19": 8,
"2020-05-20": 5,
"2020-05-21": 10,
"2020-05-23": 1
}
</textarea>
</div>
<button type="button" class="btn btn-light" id="btnPrettifyJSON">Prettify JSON</button>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switchMonthSeparator" checked>
<label class="form-check-label" for="switchMonthSeparator">Months separator</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="switchLabels" checked>
<label class="form-check-label" for="switchLabels">Labels</label>
</div>
<div class="mb-3">
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchMon" checked>
<label class="form-check-label" for="switchMon">Mon</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchTue">
<label class="form-check-label" for="switchTue">Tue</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchWed" checked>
<label class="form-check-label" for="switchWed">Wed</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchThu">
<label class="form-check-label" for="switchThu">Thu</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchFri" checked>
<label class="form-check-label" for="switchFri">Fri</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchSat">
<label class="form-check-label" for="switchSat">Sat</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="switchSun">
<label class="form-check-label" for="switchSun">Sun</label>
</div>
</div>
<a id="downloadLink" download="" href="" class="btn btn-primary" role="button">
<i class="bi bi-download"></i> Download
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

182
web/main.go Normal file
View File

@ -0,0 +1,182 @@
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"image/color"
"log"
"syscall/js"
"time"
_ "embed"
"github.com/nikolaydubina/calendarheatmap/charts"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
)
//go:embed assets/fonts/Sunflower-Medium.ttf
var defaultFontFaceBytes []byte
//go:embed assets/colorscales/green-blue-9.csv
var defaultColorScaleBytes []byte
type Renderer struct {
config charts.HeatmapConfig
img []byte
}
func (r *Renderer) PrettifyJSON(this js.Value, inputs []js.Value) interface{} {
document := js.Global().Get("document")
instr := document.Call("getElementById", "inputData").Get("value")
data := map[string]interface{}{}
if err := json.Unmarshal([]byte(instr.String()), &data); err == nil {
if out, err := json.MarshalIndent(data, "", " "); err == nil {
document.Call("getElementById", "inputData").Set("value", string(out))
}
}
return nil
}
func (r *Renderer) OnDataChange(this js.Value, inputs []js.Value) interface{} {
document := js.Global().Get("document")
instr := document.Call("getElementById", "inputData").Get("value")
data := map[string]int{}
if err := json.Unmarshal([]byte(instr.String()), &data); err == nil {
r.config.Counts = data
r.Render()
}
return nil
}
func (r *Renderer) GetFormatUpdater(format string) func(this js.Value, inputs []js.Value) interface{} {
return func(this js.Value, inputs []js.Value) interface{} {
r.config.Format = format
if format == "svg" {
js.Global().Get("document").Call("getElementById", "switchLabels").Set("disabled", "true")
js.Global().Get("document").Call("getElementById", "switchMonthSeparator").Set("disabled", "true")
} else {
js.Global().Get("document").Call("getElementById", "switchLabels").Call("removeAttribute", "disabled")
js.Global().Get("document").Call("getElementById", "switchMonthSeparator").Call("removeAttribute", "disabled")
}
r.Render()
return nil
}
}
func (r *Renderer) GetWeekdaySwitchUpdater(weekday time.Weekday) func(this js.Value, inputs []js.Value) interface{} {
return func(this js.Value, inputs []js.Value) interface{} {
r.config.ShowWeekdays[weekday] = !r.config.ShowWeekdays[weekday]
r.Render()
return nil
}
}
func (r *Renderer) ToggleLabels(this js.Value, inputs []js.Value) interface{} {
r.config.DrawLabels = !r.config.DrawLabels
r.Render()
return nil
}
func (r *Renderer) ToggleMonthSeparator(this js.Value, inputs []js.Value) interface{} {
r.config.DrawMonthSeparator = !r.config.DrawMonthSeparator
r.Render()
return nil
}
func (r *Renderer) Render() {
var output bytes.Buffer
if err := charts.WriteHeatmap(r.config, &output); err != nil {
log.Println(err)
return
}
if r.config.Format == "svg" {
img := output.String()
js.Global().Get("document").Call("getElementById", "output-container").Set("innerHTML", img)
} else {
img := js.Global().Get("document").Call("createElement", "img")
src := fmt.Sprintf("data:image/%s;base64,%s", r.config.Format, base64.StdEncoding.EncodeToString(output.Bytes()))
img.Set("src", src)
img.Set("style", "width: 100%;")
container := js.Global().Get("document").Call("getElementById", "output-container")
container.Set("innerHTML", "")
container.Call("appendChild", img)
}
// download file update button
src := fmt.Sprintf("data:image/%s;base64,%s", r.config.Format, base64.StdEncoding.EncodeToString(output.Bytes()))
link := js.Global().Get("document").Call("getElementById", "downloadLink")
link.Set("href", src)
link.Set("download", fmt.Sprintf("calendar-heatmap.%s", r.config.Format))
}
func main() {
c := make(chan bool)
colorscale, _ := charts.NewBasicColorscaleFromCSV(bytes.NewBuffer(defaultColorScaleBytes))
fontFace, _ := charts.LoadFontFace(defaultFontFaceBytes, opentype.FaceOptions{
Size: 13,
DPI: 80,
Hinting: font.HintingNone,
})
renderer := Renderer{
config: charts.HeatmapConfig{
Counts: nil,
ColorScale: colorscale,
DrawMonthSeparator: true,
DrawLabels: true,
Margin: 5,
BoxSize: 24,
MonthSeparatorWidth: 1,
MonthLabelYOffset: 5,
TextWidthLeft: 40,
TextHeightTop: 15,
TextColor: color.RGBA{100, 100, 100, 255},
BorderColor: color.RGBA{200, 200, 200, 255},
Locale: "en_US",
Format: "png",
FontFace: fontFace,
ShowWeekdays: map[time.Weekday]bool{
time.Monday: true,
time.Wednesday: true,
time.Friday: true,
},
},
}
document := js.Global().Get("document")
document.Call("getElementById", "inputConfig").Call("reset")
document.Call("getElementById", "inputData").Set("onkeyup", js.FuncOf(renderer.OnDataChange))
document.Call("getElementById", "btnPrettifyJSON").Set("onclick", js.FuncOf(renderer.PrettifyJSON))
document.Call("getElementById", "formatSVG").Set("onclick", js.FuncOf(renderer.GetFormatUpdater("svg")))
document.Call("getElementById", "formatPNG").Set("onclick", js.FuncOf(renderer.GetFormatUpdater("png")))
document.Call("getElementById", "formatJPEG").Set("onclick", js.FuncOf(renderer.GetFormatUpdater("jpeg")))
document.Call("getElementById", "switchMon").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Monday)))
document.Call("getElementById", "switchTue").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Tuesday)))
document.Call("getElementById", "switchWed").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Wednesday)))
document.Call("getElementById", "switchThu").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Thursday)))
document.Call("getElementById", "switchFri").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Friday)))
document.Call("getElementById", "switchSat").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Saturday)))
document.Call("getElementById", "switchSun").Set("onchange", js.FuncOf(renderer.GetWeekdaySwitchUpdater(time.Sunday)))
document.Call("getElementById", "switchLabels").Set("onchange", js.FuncOf(renderer.ToggleLabels))
document.Call("getElementById", "switchMonthSeparator").Set("onchange", js.FuncOf(renderer.ToggleMonthSeparator))
renderer.OnDataChange(js.Value{}, nil)
renderer.PrettifyJSON(js.Value{}, nil)
renderer.Render()
<-c
}