mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-11-28 05:00:26 +08:00
470 lines
13 KiB
Go
470 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/wavetermdev/waveterm/tsunami/app"
|
|
"github.com/wavetermdev/waveterm/tsunami/vdom"
|
|
)
|
|
|
|
var AppMeta = app.AppMeta{
|
|
Title: "Recharts Demo",
|
|
ShortDesc: "Interactive charts and data visualization using Recharts",
|
|
}
|
|
|
|
// Global atoms for config and data
|
|
var (
|
|
chartDataAtom = app.DataAtom("chartData", generateInitialData(), &app.AtomMeta{
|
|
Desc: "Chart data points for system metrics visualization",
|
|
})
|
|
chartTypeAtom = app.ConfigAtom("chartType", "line", &app.AtomMeta{
|
|
Desc: "Type of chart to display",
|
|
Enum: []string{"line", "area", "bar"},
|
|
})
|
|
isAnimatingAtom = app.ConfigAtom("isAnimating", false, &app.AtomMeta{
|
|
Desc: "Whether the chart is currently animating with live data",
|
|
})
|
|
)
|
|
|
|
type DataPoint struct {
|
|
Time int `json:"time"`
|
|
CPU float64 `json:"cpu"`
|
|
Mem float64 `json:"mem"`
|
|
Disk float64 `json:"disk"`
|
|
}
|
|
|
|
func generateInitialData() []DataPoint {
|
|
data := make([]DataPoint, 20)
|
|
for i := 0; i < 20; i++ {
|
|
data[i] = DataPoint{
|
|
Time: i,
|
|
CPU: 50 + 30*math.Sin(float64(i)*0.3) + 10*math.Sin(float64(i)*0.7),
|
|
Mem: 40 + 25*math.Cos(float64(i)*0.4) + 15*math.Sin(float64(i)*0.9),
|
|
Disk: 30 + 20*math.Sin(float64(i)*0.2) + 10*math.Cos(float64(i)*1.1),
|
|
}
|
|
}
|
|
return data
|
|
}
|
|
|
|
func generateNewDataPoint(currentData []DataPoint) DataPoint {
|
|
lastTime := 0
|
|
if len(currentData) > 0 {
|
|
lastTime = currentData[len(currentData)-1].Time
|
|
}
|
|
newTime := lastTime + 1
|
|
|
|
return DataPoint{
|
|
Time: newTime,
|
|
CPU: 50 + 30*math.Sin(float64(newTime)*0.3) + 10*math.Sin(float64(newTime)*0.7),
|
|
Mem: 40 + 25*math.Cos(float64(newTime)*0.4) + 15*math.Sin(float64(newTime)*0.9),
|
|
Disk: 30 + 20*math.Sin(float64(newTime)*0.2) + 10*math.Cos(float64(newTime)*1.1),
|
|
}
|
|
}
|
|
|
|
var InfoSection = app.DefineComponent("InfoSection", func(_ struct{}) any {
|
|
return vdom.H("div", map[string]any{
|
|
"className": "bg-blue-50 border border-blue-200 rounded-lg p-4",
|
|
},
|
|
vdom.H("h3", map[string]any{
|
|
"className": "text-lg font-semibold text-blue-900 mb-2",
|
|
}, "Recharts Integration Features"),
|
|
vdom.H("ul", map[string]any{
|
|
"className": "space-y-2 text-blue-800",
|
|
},
|
|
vdom.H("li", map[string]any{
|
|
"className": "flex items-start gap-2",
|
|
},
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-blue-500 mt-1",
|
|
}, "•"),
|
|
"Support for all major Recharts components (LineChart, AreaChart, BarChart, etc.)",
|
|
),
|
|
vdom.H("li", map[string]any{
|
|
"className": "flex items-start gap-2",
|
|
},
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-blue-500 mt-1",
|
|
}, "•"),
|
|
"Live data updates with animation support",
|
|
),
|
|
vdom.H("li", map[string]any{
|
|
"className": "flex items-start gap-2",
|
|
},
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-blue-500 mt-1",
|
|
}, "•"),
|
|
"Responsive containers that resize with the window",
|
|
),
|
|
vdom.H("li", map[string]any{
|
|
"className": "flex items-start gap-2",
|
|
},
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-blue-500 mt-1",
|
|
}, "•"),
|
|
"Full prop support for customization and styling",
|
|
),
|
|
vdom.H("li", map[string]any{
|
|
"className": "flex items-start gap-2",
|
|
},
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-blue-500 mt-1",
|
|
}, "•"),
|
|
"Uses recharts: namespace to dispatch to the recharts handler",
|
|
),
|
|
),
|
|
)
|
|
},
|
|
)
|
|
|
|
type MiniChartsProps struct {
|
|
ChartData []DataPoint `json:"chartData"`
|
|
}
|
|
|
|
var MiniCharts = app.DefineComponent("MiniCharts",
|
|
func(props MiniChartsProps) any {
|
|
return vdom.H("div", map[string]any{
|
|
"className": "grid grid-cols-1 md:grid-cols-3 gap-6 mb-6",
|
|
},
|
|
// CPU Mini Chart
|
|
vdom.H("div", map[string]any{
|
|
"className": "bg-white rounded-lg shadow-sm border p-4",
|
|
},
|
|
vdom.H("h3", map[string]any{
|
|
"className": "text-lg font-medium text-gray-900 mb-3",
|
|
}, "CPU Usage"),
|
|
vdom.H("div", map[string]any{
|
|
"className": "h-32",
|
|
},
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:LineChart", map[string]any{
|
|
"data": props.ChartData,
|
|
},
|
|
vdom.H("recharts:Line", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "cpu",
|
|
"stroke": "#8884d8",
|
|
"strokeWidth": 2,
|
|
"dot": false,
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Memory Mini Chart
|
|
vdom.H("div", map[string]any{
|
|
"className": "bg-white rounded-lg shadow-sm border p-4",
|
|
},
|
|
vdom.H("h3", map[string]any{
|
|
"className": "text-lg font-medium text-gray-900 mb-3",
|
|
}, "Memory Usage"),
|
|
vdom.H("div", map[string]any{
|
|
"className": "h-32",
|
|
},
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:AreaChart", map[string]any{
|
|
"data": props.ChartData,
|
|
},
|
|
vdom.H("recharts:Area", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "mem",
|
|
"stroke": "#82ca9d",
|
|
"fill": "#82ca9d",
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Disk Mini Chart
|
|
vdom.H("div", map[string]any{
|
|
"className": "bg-white rounded-lg shadow-sm border p-4",
|
|
},
|
|
vdom.H("h3", map[string]any{
|
|
"className": "text-lg font-medium text-gray-900 mb-3",
|
|
}, "Disk Usage"),
|
|
vdom.H("div", map[string]any{
|
|
"className": "h-32",
|
|
},
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:BarChart", map[string]any{
|
|
"data": props.ChartData,
|
|
},
|
|
vdom.H("recharts:Bar", map[string]any{
|
|
"dataKey": "disk",
|
|
"fill": "#ffc658",
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)
|
|
},
|
|
)
|
|
|
|
var App = app.DefineComponent("App",
|
|
func(_ struct{}) any {
|
|
|
|
tickerFn := func() {
|
|
if !isAnimatingAtom.Get() {
|
|
return
|
|
}
|
|
chartDataAtom.SetFn(func(currentData []DataPoint) []DataPoint {
|
|
newData := append(currentData, generateNewDataPoint(currentData))
|
|
if len(newData) > 20 {
|
|
newData = newData[1:]
|
|
}
|
|
return newData
|
|
})
|
|
}
|
|
app.UseTicker(time.Second, tickerFn, []any{})
|
|
|
|
handleStartStop := func() {
|
|
isAnimatingAtom.Set(!isAnimatingAtom.Get())
|
|
}
|
|
|
|
handleReset := func() {
|
|
chartDataAtom.Set(generateInitialData())
|
|
isAnimatingAtom.Set(false)
|
|
}
|
|
|
|
handleChartTypeChange := func(newType string) {
|
|
chartTypeAtom.Set(newType)
|
|
}
|
|
|
|
chartData := chartDataAtom.Get()
|
|
chartType := chartTypeAtom.Get()
|
|
isAnimating := isAnimatingAtom.Get()
|
|
|
|
return vdom.H("div", map[string]any{
|
|
"className": "min-h-screen bg-gray-50 p-6",
|
|
},
|
|
vdom.H("div", map[string]any{
|
|
"className": "max-w-6xl mx-auto",
|
|
},
|
|
// Header
|
|
vdom.H("div", map[string]any{
|
|
"className": "mb-8",
|
|
},
|
|
vdom.H("h1", map[string]any{
|
|
"className": "text-3xl font-bold text-gray-900 mb-2",
|
|
}, "Recharts Integration Demo"),
|
|
vdom.H("p", map[string]any{
|
|
"className": "text-gray-600",
|
|
}, "Demonstrating recharts components in Tsunami VDOM system"),
|
|
),
|
|
|
|
// Controls
|
|
vdom.H("div", map[string]any{
|
|
"className": "bg-white rounded-lg shadow-sm border p-4 mb-6",
|
|
},
|
|
vdom.H("div", map[string]any{
|
|
"className": "flex items-center gap-4 flex-wrap",
|
|
},
|
|
// Chart type selector
|
|
vdom.H("div", map[string]any{
|
|
"className": "flex items-center gap-2",
|
|
},
|
|
vdom.H("label", map[string]any{
|
|
"className": "text-sm font-medium text-gray-700",
|
|
}, "Chart Type:"),
|
|
vdom.H("select", map[string]any{
|
|
"className": "px-3 py-1 border border-gray-300 rounded-md text-sm bg-white text-gray-900 focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
"value": chartType,
|
|
"onChange": func(e vdom.VDomEvent) {
|
|
handleChartTypeChange(e.TargetValue)
|
|
},
|
|
},
|
|
vdom.H("option", map[string]any{"value": "line"}, "Line Chart"),
|
|
vdom.H("option", map[string]any{"value": "area"}, "Area Chart"),
|
|
vdom.H("option", map[string]any{"value": "bar"}, "Bar Chart"),
|
|
),
|
|
),
|
|
|
|
// Animation controls
|
|
vdom.H("button", map[string]any{
|
|
"className": vdom.Classes(
|
|
"px-4 py-2 rounded-md text-sm font-medium transition-colors",
|
|
vdom.IfElse(isAnimating,
|
|
"bg-red-500 hover:bg-red-600 text-white",
|
|
"bg-green-500 hover:bg-green-600 text-white",
|
|
),
|
|
),
|
|
"onClick": handleStartStop,
|
|
}, vdom.IfElse(isAnimating, "Stop Animation", "Start Animation")),
|
|
|
|
vdom.H("button", map[string]any{
|
|
"className": "px-4 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded-md text-sm font-medium transition-colors",
|
|
"onClick": handleReset,
|
|
}, "Reset Data"),
|
|
|
|
// Status indicator
|
|
vdom.H("div", map[string]any{
|
|
"className": "flex items-center gap-2",
|
|
},
|
|
vdom.H("div", map[string]any{
|
|
"className": vdom.Classes(
|
|
"w-2 h-2 rounded-full",
|
|
vdom.IfElse(isAnimating, "bg-green-500", "bg-gray-400"),
|
|
),
|
|
}),
|
|
vdom.H("span", map[string]any{
|
|
"className": "text-sm text-gray-600",
|
|
}, vdom.IfElse(isAnimating, "Live Updates", "Static")),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Main chart
|
|
vdom.H("div", map[string]any{
|
|
"className": "bg-white rounded-lg shadow-sm border p-6 mb-6",
|
|
},
|
|
vdom.H("h2", map[string]any{
|
|
"className": "text-xl font-semibold text-gray-900 mb-4",
|
|
}, "System Metrics Over Time"),
|
|
vdom.H("div", map[string]any{
|
|
"className": "w-full h-96",
|
|
},
|
|
// Main chart - switches based on chartType
|
|
vdom.IfElse(chartType == "line",
|
|
// Line Chart
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:LineChart", map[string]any{
|
|
"data": chartData,
|
|
},
|
|
vdom.H("recharts:CartesianGrid", map[string]any{
|
|
"strokeDasharray": "3 3",
|
|
}),
|
|
vdom.H("recharts:XAxis", map[string]any{
|
|
"dataKey": "time",
|
|
}),
|
|
vdom.H("recharts:YAxis", nil),
|
|
vdom.H("recharts:Tooltip", nil),
|
|
vdom.H("recharts:Legend", nil),
|
|
vdom.H("recharts:Line", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "cpu",
|
|
"stroke": "#8884d8",
|
|
"name": "CPU %",
|
|
}),
|
|
vdom.H("recharts:Line", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "mem",
|
|
"stroke": "#82ca9d",
|
|
"name": "Memory %",
|
|
}),
|
|
vdom.H("recharts:Line", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "disk",
|
|
"stroke": "#ffc658",
|
|
"name": "Disk %",
|
|
}),
|
|
),
|
|
),
|
|
vdom.IfElse(chartType == "area",
|
|
// Area Chart
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:AreaChart", map[string]any{
|
|
"data": chartData,
|
|
},
|
|
vdom.H("recharts:CartesianGrid", map[string]any{
|
|
"strokeDasharray": "3 3",
|
|
}),
|
|
vdom.H("recharts:XAxis", map[string]any{
|
|
"dataKey": "time",
|
|
}),
|
|
vdom.H("recharts:YAxis", nil),
|
|
vdom.H("recharts:Tooltip", nil),
|
|
vdom.H("recharts:Legend", nil),
|
|
vdom.H("recharts:Area", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "cpu",
|
|
"stackId": "1",
|
|
"stroke": "#8884d8",
|
|
"fill": "#8884d8",
|
|
"name": "CPU %",
|
|
}),
|
|
vdom.H("recharts:Area", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "mem",
|
|
"stackId": "1",
|
|
"stroke": "#82ca9d",
|
|
"fill": "#82ca9d",
|
|
"name": "Memory %",
|
|
}),
|
|
vdom.H("recharts:Area", map[string]any{
|
|
"type": "monotone",
|
|
"dataKey": "disk",
|
|
"stackId": "1",
|
|
"stroke": "#ffc658",
|
|
"fill": "#ffc658",
|
|
"name": "Disk %",
|
|
}),
|
|
),
|
|
),
|
|
// Bar Chart
|
|
vdom.H("recharts:ResponsiveContainer", map[string]any{
|
|
"width": "100%",
|
|
"height": "100%",
|
|
},
|
|
vdom.H("recharts:BarChart", map[string]any{
|
|
"data": chartData,
|
|
},
|
|
vdom.H("recharts:CartesianGrid", map[string]any{
|
|
"strokeDasharray": "3 3",
|
|
}),
|
|
vdom.H("recharts:XAxis", map[string]any{
|
|
"dataKey": "time",
|
|
}),
|
|
vdom.H("recharts:YAxis", nil),
|
|
vdom.H("recharts:Tooltip", nil),
|
|
vdom.H("recharts:Legend", nil),
|
|
vdom.H("recharts:Bar", map[string]any{
|
|
"dataKey": "cpu",
|
|
"fill": "#8884d8",
|
|
"name": "CPU %",
|
|
}),
|
|
vdom.H("recharts:Bar", map[string]any{
|
|
"dataKey": "mem",
|
|
"fill": "#82ca9d",
|
|
"name": "Memory %",
|
|
}),
|
|
vdom.H("recharts:Bar", map[string]any{
|
|
"dataKey": "disk",
|
|
"fill": "#ffc658",
|
|
"name": "Disk %",
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Mini charts row
|
|
MiniCharts(MiniChartsProps{
|
|
ChartData: chartData,
|
|
}),
|
|
|
|
// Info section
|
|
InfoSection(struct{}{}),
|
|
),
|
|
)
|
|
},
|
|
)
|