feat: replace Handler() method for HTTPServeMux() for better plugin and testing integration; remove via-plugin-picocss dependency from examples; add datastar h.H nodes for data-init, data-effect, and data-ignore-morph; update realtimechart example; other small improvements
This commit is contained in:
3
go.mod
3
go.mod
@@ -6,8 +6,6 @@ require maragu.dev/gomponents v1.2.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
|
||||||
github.com/go-via/via-plugin-picocss v0.1.0
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.32
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
github.com/starfederation/datastar-go v1.0.3
|
github.com/starfederation/datastar-go v1.0.3
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
@@ -21,7 +19,6 @@ require (
|
|||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
golang.org/x/sys v0.38.0 // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -8,10 +8,6 @@ github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUS
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
|
||||||
github.com/go-via/via-plugin-picocss v0.1.0 h1:ytVtBlfYBhidos5ub4a8liYqadz1AkeHhh7e7Paz620=
|
|
||||||
github.com/go-via/via-plugin-picocss v0.1.0/go.mod h1:5LEnLE7q8YfYY7jtH/TLPvfquB7Qt9WZ7TbKrskUW+0=
|
|
||||||
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
|
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f h1:jopqB+UTSdJGEJT8tEqYyE29zN91fi2827oLET8tl7k=
|
||||||
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
|
github.com/google/brotli/go/cbrotli v0.0.0-20230829110029-ed738e842d2f/go.mod h1:nOPhAkwVliJdNTkj3gXpljmWhjc4wCaVqbMJcPKWP4s=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||||
@@ -47,8 +43,6 @@ github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g
|
|||||||
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
|
||||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package h
|
package h
|
||||||
|
|
||||||
import "fmt"
|
func DataInit(expression string) H {
|
||||||
|
return Data("init", expression)
|
||||||
type OnClickOpts string
|
}
|
||||||
|
|
||||||
func OnClick(actionid string, opt ...OnClickOpts) H {
|
func DataEffect(expression string) H {
|
||||||
return Data("on:click", fmt.Sprintf("@get('/_action/%s')", actionid))
|
return Data("effect", expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DataIgnoreMorph() H {
|
||||||
|
return Attr("data-ignore-morph")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
## ADR
|
# ADR
|
||||||
|
|
||||||
- Support Multiple Rooms
|
- Support Multiple Rooms
|
||||||
Not single chat room toy problem.
|
Not single chat room toy problem.
|
||||||
|
|
||||||
- Rooms are generic
|
- Rooms are generic
|
||||||
They know nothing of their data. Just store it. Reusable for different usecases.
|
They know nothing of their data. Just store it. Reusable for different usecases.
|
||||||
|
|
||||||
- Server controls push frequency
|
- Server controls push frequency
|
||||||
Debounce to every 400ms, if dirty.
|
Debounce to every 400ms, if dirty.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-via/via"
|
"github.com/go-via/via"
|
||||||
"github.com/go-via/via-plugin-picocss/picocss"
|
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||||
"github.com/go-via/via/h"
|
"github.com/go-via/via/h"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,7 +15,9 @@ func main() {
|
|||||||
DocumentTitle: "Live Reload Demo",
|
DocumentTitle: "Live Reload Demo",
|
||||||
DevMode: true,
|
DevMode: true,
|
||||||
LogLvl: via.LogLevelDebug,
|
LogLvl: via.LogLevelDebug,
|
||||||
Plugins: []via.Plugin{picocss.Default},
|
Plugins: []via.Plugin{
|
||||||
|
// picocss.Default
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
v.Page("/", func(c *via.Context) {
|
v.Page("/", func(c *via.Context) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-via/via"
|
"github.com/go-via/via"
|
||||||
"github.com/go-via/via-plugin-picocss/picocss"
|
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||||
. "github.com/go-via/via/h"
|
. "github.com/go-via/via/h"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ func main() {
|
|||||||
v := via.New()
|
v := via.New()
|
||||||
|
|
||||||
v.Config(via.Options{
|
v.Config(via.Options{
|
||||||
Plugins: []via.Plugin{picocss.Default},
|
// Plugins: []via.Plugin{picocss.Default},
|
||||||
})
|
})
|
||||||
|
|
||||||
v.Page("/counters/{counter_id}/{start_at_step}", func(c *via.Context) {
|
v.Page("/counters/{counter_id}/{start_at_step}", func(c *via.Context) {
|
||||||
|
|||||||
BIN
internal/examples/pathparams/pathparams
Executable file
BIN
internal/examples/pathparams/pathparams
Executable file
Binary file not shown.
@@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-via/via"
|
"github.com/go-via/via"
|
||||||
"github.com/go-via/via-plugin-picocss/picocss"
|
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||||
"github.com/go-via/via/h"
|
"github.com/go-via/via/h"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ func main() {
|
|||||||
DocumentTitle: "Via Counter",
|
DocumentTitle: "Via Counter",
|
||||||
// Plugin is placed here. Use picocss.WithOptions(pococss.Options) to add the plugin
|
// Plugin is placed here. Use picocss.WithOptions(pococss.Options) to add the plugin
|
||||||
// with a different color theme or to enable a classes for a wide range of colors.
|
// with a different color theme or to enable a classes for a wide range of colors.
|
||||||
Plugins: []via.Plugin{
|
// Plugins: []via.Plugin{
|
||||||
picocss.Default,
|
// picocss.Default,
|
||||||
},
|
// },
|
||||||
})
|
})
|
||||||
|
|
||||||
v.Page("/", func(c *via.Context) {
|
v.Page("/", func(c *via.Context) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PicoCSSPlugin(v *via.V) {
|
func PicoCSSPlugin(v *via.V) {
|
||||||
v.HandleFunc("GET /_plugins/picocss/assets/style.css", func(w http.ResponseWriter, r *http.Request) {
|
v.HTTPServeMux().HandleFunc("GET /_plugins/picocss/assets/style.css", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/css")
|
w.Header().Set("Content-Type", "text/css")
|
||||||
_, _ = w.Write(picoCSSFile)
|
_, _ = w.Write(picoCSSFile)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-via/via"
|
"github.com/go-via/via"
|
||||||
"github.com/go-via/via-plugin-picocss/picocss"
|
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||||
"github.com/go-via/via/h"
|
"github.com/go-via/via/h"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ func main() {
|
|||||||
LogLvl: via.LogLevelDebug,
|
LogLvl: via.LogLevelDebug,
|
||||||
DevMode: true,
|
DevMode: true,
|
||||||
Plugins: []via.Plugin{
|
Plugins: []via.Plugin{
|
||||||
picocss.Default,
|
// picocss.Default,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -26,8 +26,42 @@ func main() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
v.Page("/", func(c *via.Context) {
|
v.Page("/", func(c *via.Context) {
|
||||||
chartComp := c.Component(chartCompFn)
|
|
||||||
|
|
||||||
|
isLive := true
|
||||||
|
|
||||||
|
isLiveSig := c.Signal("on")
|
||||||
|
|
||||||
|
refreshRate := c.Signal("24")
|
||||||
|
|
||||||
|
computedTickDuration := func() time.Duration {
|
||||||
|
return 1000 / time.Duration(refreshRate.Int()) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData := c.OnInterval(computedTickDuration(), func() {
|
||||||
|
ts := time.Now().UnixMilli()
|
||||||
|
val := rand.ExpFloat64() * 10
|
||||||
|
|
||||||
|
c.ExecScript(fmt.Sprintf(`
|
||||||
|
if (myChart) {
|
||||||
|
myChart.appendData({seriesIndex: 0, data: [[%d, %f]]});
|
||||||
|
myChart.setOption({},{notMerge:false,lazyUpdate:true});
|
||||||
|
};
|
||||||
|
`, ts, val))
|
||||||
|
})
|
||||||
|
updateData.Start()
|
||||||
|
|
||||||
|
updateRefreshRate := c.Action(func() {
|
||||||
|
updateData.UpdateInterval(computedTickDuration())
|
||||||
|
})
|
||||||
|
|
||||||
|
toggleIsLive := c.Action(func() {
|
||||||
|
isLive = isLiveSig.Bool()
|
||||||
|
if isLive {
|
||||||
|
updateData.Start()
|
||||||
|
} else {
|
||||||
|
updateData.Stop()
|
||||||
|
}
|
||||||
|
})
|
||||||
c.View(func() h.H {
|
c.View(func() h.H {
|
||||||
return h.Div(h.Style("overflow-x:hidden"),
|
return h.Div(h.Style("overflow-x:hidden"),
|
||||||
h.Section(h.Class("container"),
|
h.Section(h.Class("container"),
|
||||||
@@ -40,144 +74,100 @@ func main() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
chartComp(),
|
h.Div(
|
||||||
|
h.Div(h.ID("chart"), h.DataIgnoreMorph(), h.Style("width:100%;height:400px;"),
|
||||||
|
h.Script(h.Raw(`
|
||||||
|
var 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: 0, // affects updates/redraws
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: function (pt) {
|
||||||
|
return [pt[0], '10%'];
|
||||||
|
},
|
||||||
|
syncStrategy: 'closestSampledPoint',
|
||||||
|
backgroundColor: prefersDark.matches ? '#13171fc0' : '#eeeeeec0',
|
||||||
|
extraCssText: 'backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: '📈 Real-Time Chart Example'
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'time',
|
||||||
|
boundaryGap: false,
|
||||||
|
axisLabel: {
|
||||||
|
hideOverlap: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
boundaryGap: [0, '100%'],
|
||||||
|
min: 0,
|
||||||
|
max: 100
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 1,
|
||||||
|
end: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Fake Data',
|
||||||
|
type: 'line',
|
||||||
|
symbol: 'none',
|
||||||
|
sampling: 'max',
|
||||||
|
itemStyle: {
|
||||||
|
color: '#e8ae01'
|
||||||
|
},
|
||||||
|
lineStyle: { color: '#e8ae01'},
|
||||||
|
areaStyle: {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: '#fecc63'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: '#c79400'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
},
|
||||||
|
large: true,
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
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"), isLiveSig.Bind(), toggleIsLive.OnChange()),
|
||||||
|
),
|
||||||
|
h.Label(h.Text("Refresh Rate (Hz) ― "), refreshRate.Text(),
|
||||||
|
h.Input(h.Type("range"), h.Attr("min", "1"), h.Attr("max", "200"), refreshRate.Bind(), updateRefreshRate.OnChange()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
v.Start()
|
v.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func chartCompFn(c *via.Context) {
|
|
||||||
isLive := true
|
|
||||||
|
|
||||||
isLiveSig := c.Signal("on")
|
|
||||||
|
|
||||||
refreshRate := c.Signal("24")
|
|
||||||
|
|
||||||
computedTickDuration := func() time.Duration {
|
|
||||||
return 1000 / time.Duration(refreshRate.Int()) * time.Millisecond
|
|
||||||
}
|
|
||||||
|
|
||||||
updateData := c.OnInterval(computedTickDuration(), func() {
|
|
||||||
ts := time.Now().UnixMilli()
|
|
||||||
val := rand.ExpFloat64() * 10
|
|
||||||
|
|
||||||
c.ExecScript(fmt.Sprintf(`
|
|
||||||
if (myChart) {
|
|
||||||
myChart.appendData({seriesIndex: 0, data: [[%d, %f]]});
|
|
||||||
myChart.setOption({
|
|
||||||
},{
|
|
||||||
notMerge:false,
|
|
||||||
lazyUpdate:true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
`, ts, val))
|
|
||||||
})
|
|
||||||
updateData.Start()
|
|
||||||
|
|
||||||
updateRefreshRate := c.Action(func() {
|
|
||||||
updateData.UpdateInterval(computedTickDuration())
|
|
||||||
})
|
|
||||||
|
|
||||||
toggleIsLive := c.Action(func() {
|
|
||||||
isLive = isLiveSig.Bool()
|
|
||||||
if isLive {
|
|
||||||
updateData.Start()
|
|
||||||
} else {
|
|
||||||
updateData.Stop()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
c.View(func() h.H {
|
|
||||||
return h.Div(h.Div(h.ID("chart"), h.Style("width:100%;height:400px;"),
|
|
||||||
h.Script(h.Raw(`
|
|
||||||
var 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: 0, // affects updates/redraws
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
position: function (pt) {
|
|
||||||
return [pt[0], '10%'];
|
|
||||||
},
|
|
||||||
syncStrategy: 'closestSampledPoint',
|
|
||||||
backgroundColor: prefersDark.matches ? '#13171fc0' : '#eeeeeec0',
|
|
||||||
extraCssText: 'backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);'
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
left: 'center',
|
|
||||||
text: '📈 Real-Time Chart Example'
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'time',
|
|
||||||
boundaryGap: false,
|
|
||||||
axisLabel: {
|
|
||||||
hideOverlap: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
boundaryGap: [0, '100%'],
|
|
||||||
min: 0,
|
|
||||||
max: 100
|
|
||||||
},
|
|
||||||
dataZoom: [
|
|
||||||
{
|
|
||||||
type: 'inside',
|
|
||||||
start: 1,
|
|
||||||
end: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
start: 0,
|
|
||||||
end: 100
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'Fake Data',
|
|
||||||
type: 'line',
|
|
||||||
symbol: 'none',
|
|
||||||
sampling: 'max',
|
|
||||||
itemStyle: {
|
|
||||||
color: '#e8ae01'
|
|
||||||
},
|
|
||||||
lineStyle: { color: '#e8ae01'},
|
|
||||||
areaStyle: {
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{
|
|
||||||
offset: 0,
|
|
||||||
color: '#fecc63'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: '#c79400'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
},
|
|
||||||
large: true,
|
|
||||||
data: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
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"), isLiveSig.Bind(), toggleIsLive.OnChange()),
|
|
||||||
),
|
|
||||||
h.Label(h.Text("Refresh Rate (Hz) ― "), refreshRate.Text(),
|
|
||||||
h.Input(h.Type("range"), h.Attr("min", "1"), h.Attr("max", "200"), refreshRate.Bind(), updateRefreshRate.OnChange()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
22
via.go
22
via.go
@@ -1,5 +1,6 @@
|
|||||||
// Package via provides a reactive web framework for Go.
|
// Package via provides a reactive, real-time engine for creating Go web
|
||||||
// It lets you build live, type-safe web interfaces without JavaScript.
|
// applications. It lets you build live, type-safe web interfaces without
|
||||||
|
// JavaScript.
|
||||||
//
|
//
|
||||||
// Via unifies routing, state, and UI reactivity through a simple mental model:
|
// Via unifies routing, state, and UI reactivity through a simple mental model:
|
||||||
// Go on the server — HTML in the browser — updated in real time via Datastar.
|
// Go on the server — HTML in the browser — updated in real time via Datastar.
|
||||||
@@ -231,21 +232,18 @@ func (v *V) getCtx(id string) (*Context, error) {
|
|||||||
return nil, fmt.Errorf("ctx '%s' not found", id)
|
return nil, fmt.Errorf("ctx '%s' not found", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleFunc registers the HTTP handler function for a given pattern. The handler function panics if
|
|
||||||
// in conflict with another registered handler with the same pattern.
|
|
||||||
func (v *V) HandleFunc(pattern string, f http.HandlerFunc) {
|
|
||||||
v.mux.HandleFunc(pattern, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start starts the Via HTTP server on the given address.
|
// Start starts the Via HTTP server on the given address.
|
||||||
func (v *V) Start() {
|
func (v *V) Start() {
|
||||||
v.logInfo(nil, "via started at [%s]", v.cfg.ServerAddress)
|
v.logInfo(nil, "via started at [%s]", v.cfg.ServerAddress)
|
||||||
log.Fatalf("[fatal] %v", http.ListenAndServe(v.cfg.ServerAddress, v.mux))
|
log.Fatalf("[fatal] %v", http.ListenAndServe(v.cfg.ServerAddress, v.mux))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler returns the underlying http.Handler for use with custom servers or testing.
|
// HTTPServeMux returns the underlying HTTP request multiplexer to enable user extentions, middleware and
|
||||||
// This enables integration with test frameworks like gost-dom/browser for SSE/Datastar testing.
|
// plugins. It also enables integration with test frameworks like gost-dom/browser for SSE/Datastar testing.
|
||||||
func (v *V) Handler() http.Handler {
|
//
|
||||||
|
// IMPORTANT. The returned *http.ServeMux can only be modified during initialization, before calling via.Start().
|
||||||
|
// Concurrent handler registration is not safe.
|
||||||
|
func (v *V) HTTPServeMux() *http.ServeMux {
|
||||||
return v.mux
|
return v.mux
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,14 +421,12 @@ func New() *V {
|
|||||||
if sse.Context().Err() == nil {
|
if sse.Context().Err() == nil {
|
||||||
v.logErr(c, "PatchElements failed: %v", err)
|
v.logErr(c, "PatchElements failed: %v", err)
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case patchTypeSignals:
|
case patchTypeSignals:
|
||||||
if err := sse.PatchSignals([]byte(patch.content)); err != nil {
|
if err := sse.PatchSignals([]byte(patch.content)); err != nil {
|
||||||
if sse.Context().Err() == nil {
|
if sse.Context().Err() == nil {
|
||||||
v.logErr(c, "PatchSignals failed: %v", err)
|
v.logErr(c, "PatchSignals failed: %v", err)
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case patchTypeScript:
|
case patchTypeScript:
|
||||||
if err := sse.ExecuteScript(patch.content, datastar.WithExecuteScriptAutoRemove(true)); err != nil {
|
if err := sse.ExecuteScript(patch.content, datastar.WithExecuteScriptAutoRemove(true)); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user