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
|
// 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 {
|
func (a *actionTrigger) OnClick() h.H {
|
||||||
return h.Data("on:click", fmt.Sprintf("@get('/_action/%s')", a.id))
|
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)
|
return gh.Class(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Role(v string) H {
|
||||||
|
return gh.Role(v)
|
||||||
|
}
|
||||||
|
|
||||||
// Data attributes automatically have their name prefixed with "data-".
|
// Data attributes automatically have their name prefixed with "data-".
|
||||||
func Data(name, v string) H {
|
func Data(name, v string) H {
|
||||||
return gh.Data(name, v)
|
return gh.Data(name, v)
|
||||||
|
|||||||
8
h/h.go
8
h/h.go
@@ -11,6 +11,7 @@ package h
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
g "maragu.dev/gomponents"
|
g "maragu.dev/gomponents"
|
||||||
gc "maragu.dev/gomponents/components"
|
gc "maragu.dev/gomponents/components"
|
||||||
)
|
)
|
||||||
@@ -50,6 +51,13 @@ func Attr(name string, value ...string) H {
|
|||||||
return g.Attr(name, value...)
|
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
|
// HTML5Props defines properties for HTML5 pages. Title is set always set, Description
|
||||||
// and Language elements only if the strings are non-empty.
|
// and Language elements only if the strings are non-empty.
|
||||||
type HTML5Props struct {
|
type HTML5Props struct {
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import (
|
|||||||
func retype(nodes []H) []g.Node {
|
func retype(nodes []H) []g.Node {
|
||||||
list := make([]g.Node, len(nodes))
|
list := make([]g.Node, len(nodes))
|
||||||
for i, node := range nodes {
|
for i, node := range nodes {
|
||||||
|
if node == nil {
|
||||||
|
list[i] = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
list[i] = node.(g.Node)
|
list[i] = node.(g.Node)
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
|
|||||||
@@ -38,33 +38,47 @@ func chartCompFn(c *via.Context) {
|
|||||||
data := make([]float64, 1000)
|
data := make([]float64, 1000)
|
||||||
labels := make([]string, 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() {
|
go func() {
|
||||||
tkr := time.NewTicker(60 * time.Millisecond)
|
|
||||||
defer tkr.Stop()
|
defer tkr.Stop()
|
||||||
for range tkr.C {
|
for range tkr.C {
|
||||||
labels = append(labels[1:], time.Now().Format("15:04:05.000"))
|
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)
|
labelsTxt, _ := json.Marshal(labels)
|
||||||
dataTxt, _ := json.Marshal(data)
|
dataTxt, _ := json.Marshal(data)
|
||||||
|
|
||||||
|
if isLive {
|
||||||
c.ExecScript(fmt.Sprintf(`
|
c.ExecScript(fmt.Sprintf(`
|
||||||
if (myChart)
|
if (myChart)
|
||||||
myChart.setOption({
|
myChart.setOption({
|
||||||
xAxis: [{data: %s}],
|
xAxis: [{data: %s}],
|
||||||
series:[{data: %s}]
|
series:[{data: %s}]
|
||||||
});
|
},{notMerge:false,lazyUpdate:true});
|
||||||
`, labelsTxt, dataTxt))
|
`, labelsTxt, dataTxt))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
c.View(func() h.H {
|
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(`
|
h.Script(h.Raw(`
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
var myChart = echarts.init(document.getElementById('chart'), prefersDark.matches ? 'dark' : 'light');
|
var myChart = echarts.init(document.getElementById('chart'), prefersDark.matches ? 'dark' : 'light');
|
||||||
var option = {
|
var option = {
|
||||||
backgroundColor: prefersDark.matches ? 'transparent' : '#ffffff',
|
backgroundColor: prefersDark.matches ? 'transparent' : '#ffffff',
|
||||||
animationDurationUpdate: 60, // affects updates/redraws
|
animationDurationUpdate: 0, // affects updates/redraws
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
position: function (pt) {
|
position: function (pt) {
|
||||||
@@ -75,15 +89,6 @@ func chartCompFn(c *via.Context) {
|
|||||||
left: 'center',
|
left: 'center',
|
||||||
text: 'Large Area Chart'
|
text: 'Large Area Chart'
|
||||||
},
|
},
|
||||||
toolbox: {
|
|
||||||
feature: {
|
|
||||||
dataZoom: {
|
|
||||||
yAxisIndex: 'none'
|
|
||||||
},
|
|
||||||
restore: {},
|
|
||||||
saveAsImage: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
@@ -131,6 +136,22 @@ func chartCompFn(c *via.Context) {
|
|||||||
};
|
};
|
||||||
option && myChart.setOption(option);
|
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:
|
// Example:
|
||||||
//
|
//
|
||||||
// h.Div(h.Text("x: "), mysignal.Text())
|
// h.Div(mysignal.Text())
|
||||||
func (s *signal) Text() h.H {
|
func (s *signal) Text() h.H {
|
||||||
return h.Data("text", "$"+s.id)
|
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 := v.cfg.DocumentHeadIncludes
|
||||||
headElements = append(headElements, h.Meta(h.Data("signals", fmt.Sprintf("{'via-ctx':'%s'}", id))))
|
headElements = append(headElements, h.Meta(h.Data("signals", fmt.Sprintf("{'via-ctx':'%s'}", id))))
|
||||||
headElements = append(headElements, h.Meta(h.Data("init", "@get('/_sse')")))
|
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 {
|
for _, el := range v.cfg.DocumentBodyIncludes {
|
||||||
bottomBodyElements = append(bottomBodyElements, el)
|
bottomBodyElements = append(bottomBodyElements, el)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user