feat: improve real-time chart example; add small refinements to via core files
This commit is contained in:
@@ -12,7 +12,13 @@ type actionTrigger struct {
|
||||
}
|
||||
|
||||
// OnClick returns a via.h DOM node that triggers on click. It can be added
|
||||
// to other nodes in a view.
|
||||
// to element nodes in a view.
|
||||
func (a *actionTrigger) OnClick() h.H {
|
||||
return h.Data("on:click", fmt.Sprintf("@get('/_action/%s')", a.id))
|
||||
}
|
||||
|
||||
// OnChange returns a via.h DOM node that triggers on input change. It can be added
|
||||
// to element nodes in a view.
|
||||
func (a *actionTrigger) OnChange() h.H {
|
||||
return h.Data("on:change__debounce.200ms", fmt.Sprintf("@get('/_action/%s')", a.id))
|
||||
}
|
||||
|
||||
@@ -34,6 +34,10 @@ func Class(v string) H {
|
||||
return gh.Class(v)
|
||||
}
|
||||
|
||||
func Role(v string) H {
|
||||
return gh.Role(v)
|
||||
}
|
||||
|
||||
// Data attributes automatically have their name prefixed with "data-".
|
||||
func Data(name, v string) H {
|
||||
return gh.Data(name, v)
|
||||
|
||||
8
h/h.go
8
h/h.go
@@ -11,6 +11,7 @@ package h
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
gc "maragu.dev/gomponents/components"
|
||||
)
|
||||
@@ -50,6 +51,13 @@ func Attr(name string, value ...string) H {
|
||||
return g.Attr(name, value...)
|
||||
}
|
||||
|
||||
func If(condition bool, n H) H {
|
||||
if condition {
|
||||
return n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTML5Props defines properties for HTML5 pages. Title is set always set, Description
|
||||
// and Language elements only if the strings are non-empty.
|
||||
type HTML5Props struct {
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
func retype(nodes []H) []g.Node {
|
||||
list := make([]g.Node, len(nodes))
|
||||
for i, node := range nodes {
|
||||
if node == nil {
|
||||
list[i] = nil
|
||||
continue
|
||||
}
|
||||
list[i] = node.(g.Node)
|
||||
}
|
||||
return list
|
||||
|
||||
@@ -38,33 +38,47 @@ func chartCompFn(c *via.Context) {
|
||||
data := make([]float64, 1000)
|
||||
labels := make([]string, 1000)
|
||||
|
||||
isLive := false
|
||||
refreshRate := c.Signal(1)
|
||||
tkr := time.NewTicker(1000 * time.Millisecond)
|
||||
|
||||
updateRefreshRate := c.Action(func() {
|
||||
tkr.Reset(1000 / time.Duration(refreshRate.Int()) * time.Millisecond)
|
||||
})
|
||||
|
||||
toggleIsLive := c.Action(func() {
|
||||
isLive = !isLive
|
||||
})
|
||||
|
||||
go func() {
|
||||
tkr := time.NewTicker(60 * time.Millisecond)
|
||||
defer tkr.Stop()
|
||||
for range tkr.C {
|
||||
labels = append(labels[1:], time.Now().Format("15:04:05.000"))
|
||||
data = append(data[1:], rand.Float64()*1000)
|
||||
data = append(data[1:], rand.Float64()*10)
|
||||
labelsTxt, _ := json.Marshal(labels)
|
||||
dataTxt, _ := json.Marshal(data)
|
||||
|
||||
if isLive {
|
||||
c.ExecScript(fmt.Sprintf(`
|
||||
if (myChart)
|
||||
myChart.setOption({
|
||||
xAxis: [{data: %s}],
|
||||
series:[{data: %s}]
|
||||
});
|
||||
},{notMerge:false,lazyUpdate:true});
|
||||
`, labelsTxt, dataTxt))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
c.View(func() h.H {
|
||||
return h.Div(h.ID("chart"), h.Style("width:100%;height:400px;"),
|
||||
return h.Div(
|
||||
h.Div(h.ID("chart"), h.Style("width:100%;height:400px;"),
|
||||
h.Script(h.Raw(`
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
var myChart = echarts.init(document.getElementById('chart'), prefersDark.matches ? 'dark' : 'light');
|
||||
var option = {
|
||||
backgroundColor: prefersDark.matches ? 'transparent' : '#ffffff',
|
||||
animationDurationUpdate: 60, // affects updates/redraws
|
||||
animationDurationUpdate: 0, // affects updates/redraws
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
position: function (pt) {
|
||||
@@ -75,15 +89,6 @@ func chartCompFn(c *via.Context) {
|
||||
left: 'center',
|
||||
text: 'Large Area Chart'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
@@ -131,6 +136,22 @@ func chartCompFn(c *via.Context) {
|
||||
};
|
||||
option && myChart.setOption(option);
|
||||
`)),
|
||||
h.Section(
|
||||
h.Article(
|
||||
h.H5(h.Text("Controls")),
|
||||
h.Hr(),
|
||||
h.Div(h.Class("grid"),
|
||||
h.FieldSet(
|
||||
h.Legend(h.Text("Live Data")),
|
||||
h.Input(h.Type("checkbox"), h.Role("switch"), toggleIsLive.OnChange()),
|
||||
),
|
||||
h.Label(h.ID("refresh-rate-input"), h.Span(h.Text("Refresh Rate (")), h.Span(refreshRate.Text()), h.Span(h.Text(" Hz)")),
|
||||
h.Input(h.Type("range"), h.Attr("min", "1"), h.Attr("max", "200"), refreshRate.Bind(), updateRefreshRate.OnChange()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func (s *signal) Bind() h.H {
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// h.Div(h.Text("x: "), mysignal.Text())
|
||||
// h.Div(mysignal.Text())
|
||||
func (s *signal) Text() h.H {
|
||||
return h.Data("text", "$"+s.id)
|
||||
}
|
||||
|
||||
2
via.go
2
via.go
@@ -106,7 +106,7 @@ func (v *via) Page(route string, composeContext func(c *Context)) {
|
||||
headElements := v.cfg.DocumentHeadIncludes
|
||||
headElements = append(headElements, h.Meta(h.Data("signals", fmt.Sprintf("{'via-ctx':'%s'}", id))))
|
||||
headElements = append(headElements, h.Meta(h.Data("init", "@get('/_sse')")))
|
||||
bottomBodyElements := []h.H{h.Div(h.ID(c.id), c.view())}
|
||||
bottomBodyElements := []h.H{c.view()}
|
||||
for _, el := range v.cfg.DocumentBodyIncludes {
|
||||
bottomBodyElements = append(bottomBodyElements, el)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user