Skip to content
Merged

VDom 8 #1202

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 169 additions & 144 deletions cmd/wsh/cmd/wshcmd-html.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package cmd
import (
"context"
"log"
"time"

"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/vdom"
Expand All @@ -18,7 +17,7 @@ import (
)

var htmlCmdNewBlock bool
var GlobalVDomClient *vdomclient.Client
var HtmlVDomClient *vdomclient.Client = vdomclient.MakeClient(&vdom.VDomBackendOpts{CloseOnCtrlC: true})

func init() {
htmlCmd.Flags().BoolVarP(&htmlCmdNewBlock, "newblock", "n", false, "create a new block")
Expand All @@ -32,172 +31,198 @@ var htmlCmd = &cobra.Command{
RunE: htmlRun,
}

func StyleTag(ctx context.Context, props map[string]any) any {
return vdom.Bind(`
<style>
.root {
padding: 10px;
}

.background {
display: flex;
align-items: center;
width: 100%;

.background-inner {
max-width: 300px;

.bg-item {
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;

&:hover {
background-color: var(--button-grey-hover-bg);
}

.bg-preview {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
border: 1px solid #777;
}

.bg-label {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
</style>
`, nil)
// Prop Types
type BgItemProps struct {
Bg string `json:"bg"`
Label string `json:"label"`
OnClick func() `json:"onClick"`
}

type BgItemProps struct {
Bg string
Label string
type BgListProps struct {
Items []BgItem `json:"items"`
}

type BgItem struct {
Bg string `json:"bg"`
Label string `json:"label"`
}

func BgItemTag(ctx context.Context, props BgItemProps) any {
clickFn := func() {
log.Printf("bg item clicked %q\n", props.Bg)
blockInfo, err := wshclient.BlockInfoCommand(GlobalVDomClient.RpcClient, GlobalVDomClient.RpcContext.BlockId, nil)
if err != nil {
log.Printf("error getting block info: %v\n", err)
return
// Components
var Style = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "Style",
func(ctx context.Context, _ struct{}) any {
return vdom.E("style", nil, `
.root {
padding: 10px;
}

.background {
display: flex;
align-items: center;
width: 100%;
}

.background-inner {
max-width: 300px;
}

.bg-item {
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
}

.bg-item:hover {
background-color: var(--button-grey-hover-bg);
}

.bg-preview {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
border: 1px solid #777;
}

.bg-label {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`)
},
)

var BgItemTag = vdomclient.DefineComponent[BgItemProps](HtmlVDomClient, "BgItem",
func(ctx context.Context, props BgItemProps) any {
return vdom.E("div",
vdom.P("className", "bg-item"),
vdom.P("onClick", props.OnClick),
vdom.E("div",
vdom.P("className", "bg-preview"),
vdom.PStyle("background", props.Bg),
),
vdom.E("div",
vdom.P("className", "bg-label"),
props.Label,
),
)
},
)

var BgList = vdomclient.DefineComponent[BgListProps](HtmlVDomClient, "BgList",
func(ctx context.Context, props BgListProps) any {
setBackground := func(bg string) func() {
return func() {
blockInfo, err := wshclient.BlockInfoCommand(HtmlVDomClient.RpcClient, HtmlVDomClient.RpcContext.BlockId, nil)
if err != nil {
log.Printf("error getting block info: %v\n", err)
return
}
err = wshclient.SetMetaCommand(HtmlVDomClient.RpcClient, wshrpc.CommandSetMetaData{
ORef: waveobj.ORef{OType: "tab", OID: blockInfo.TabId},
Meta: map[string]any{"bg": bg},
}, nil)
if err != nil {
log.Printf("error setting meta: %v\n", err)
}
}
}
log.Printf("block info: tabid=%q\n", blockInfo.TabId)
err = wshclient.SetMetaCommand(GlobalVDomClient.RpcClient, wshrpc.CommandSetMetaData{
ORef: waveobj.ORef{OType: "tab", OID: blockInfo.TabId},
Meta: map[string]any{"bg": props.Bg},
}, nil)
if err != nil {
log.Printf("error setting meta: %v\n", err)

items := make([]*vdom.VDomElem, 0, len(props.Items))
for _, item := range props.Items {
items = append(items, BgItemTag(BgItemProps{
Bg: item.Bg,
Label: item.Label,
OnClick: setBackground(item.Bg),
}))
}
// wshclient.SetMetaCommand(GlobalVDomClient.RpcClient)
}
params := map[string]any{
"bg": props.Bg,
"label": props.Label,
"clickHandler": clickFn,
}
return vdom.Bind(`
<div className="bg-item" onClick="#param:clickHandler">
<div className="bg-preview" style="background: #param:bg"></div>
<div className="bg-label"><bindparam key="label"/></div>
</div>`, params)
}

func AllBgItemsTag(ctx context.Context, props map[string]any) any {
items := []map[string]any{
{"bg": nil, "label": "default"},
{"bg": "#ff0000", "label": "red"},
{"bg": "#00ff00", "label": "green"},
{"bg": "#0000ff", "label": "blue"},
}
bgElems := make([]*vdom.VDomElem, 0)
for _, item := range items {
elem := vdom.E("BgItemTag", item)
bgElems = append(bgElems, elem)
}
return vdom.Bind(`
<div className="background">
<div className="background-inner">
<bindparam key="bgElems"/>
</div>
</div>
`, map[string]any{"bgElems": bgElems})
}
return vdom.E("div",
vdom.P("className", "background"),
vdom.E("div",
vdom.P("className", "background-inner"),
items,
),
)
},
)

func MakeVDom() *vdom.VDomElem {
vdomStr := `
<div className="root">
<StyleTag/>
<h1>Set Background</h1>
<div>
<wave:markdown text="*quick vdom application to set background colors*"/>
</div>
<div>
<AllBgItemsTag/>
</div>
<div>
<img style="width: 100%; height: 100%; max-width: 300px; max-height: 300px; object-fit: contain;" src="vdom:///test.png"/>
</div>
</div>
`
elem := vdom.Bind(vdomStr, nil)
return elem
}
var App = vdomclient.DefineComponent[struct{}](HtmlVDomClient, "App",
func(ctx context.Context, _ struct{}) any {
inputText, setInputText := vdom.UseState(ctx, "start")

func GlobalEventHandler(client *vdomclient.Client, event vdom.VDomEvent) {
if event.EventType == "clickinc" {
client.SetAtomVal("num", client.GetAtomVal("num").(int)+1)
return
}
}
bgItems := []BgItem{
{Bg: "", Label: "default"},
{Bg: "#ff0000", Label: "red"},
{Bg: "#00ff00", Label: "green"},
{Bg: "#0000ff", Label: "blue"},
}

return vdom.E("div",
vdom.P("className", "root"),
Style(struct{}{}),
vdom.E("h1", nil, "Set Background"),
vdom.E("div", nil,
vdom.E("wave:markdown",
vdom.P("text", "*quick vdom application to set background colors*"),
),
),
vdom.E("div", nil,
BgList(BgListProps{Items: bgItems}),
),
vdom.E("div", nil,
vdom.E("img",
vdom.P("style", "width: 100%; height: 100%; max-width: 300px; max-height: 300px; object-fit: contain;"),
vdom.P("src", "vdom:///test.png"),
),
),
vdom.E("div", nil,
vdom.E("input",
vdom.P("type", "text"),
vdom.P("value", inputText),
vdom.P("onChange", func(e vdom.VDomEvent) {
setInputText(e.TargetValue)
}),
),
vdom.E("div", nil,
"text ", inputText,
),
),
)
},
)

func htmlRun(cmd *cobra.Command, args []string) error {
WriteStderr("running wsh html %q\n", RpcContext.BlockId)

client, err := vdomclient.MakeClient(&vdom.VDomBackendOpts{CloseOnCtrlC: true})
client := HtmlVDomClient
err := client.Connect()
if err != nil {
return err
}
GlobalVDomClient = client
client.SetGlobalEventHandler(GlobalEventHandler)
log.Printf("created client: %v\n", client)
client.RegisterComponent("StyleTag", StyleTag)
client.RegisterComponent("BgItemTag", BgItemTag)
client.RegisterComponent("AllBgItemsTag", AllBgItemsTag)

// Set up the root component
client.SetRootElem(App(struct{}{}))

// Set up file handler
client.RegisterFileHandler("/test.png", "~/Downloads/IMG_1939.png")
client.SetRootElem(MakeVDom())

// Create the VDOM context
err = client.CreateVDomContext(&vdom.VDomTarget{NewBlock: htmlCmdNewBlock})
if err != nil {
return err
}
log.Printf("created context\n")

// Handle shutdown
go func() {
<-client.DoneCh
wshutil.DoShutdown("vdom closed by FE", 0, true)
}()
log.Printf("created vdom context\n")
go func() {
time.Sleep(5 * time.Second)
log.Printf("updating text\n")
client.SetAtomVal("text", "updated text")
err := client.SendAsyncInitiation()
if err != nil {
log.Printf("error sending async initiation: %v\n", err)
}
}()

<-client.DoneCh
return nil
}
Loading