Moved web-client from gopherjs-vue to hvue. Added tab component. ToDo: package vue.js

This commit is contained in:
mame82 2018-07-18 21:55:44 +00:00
parent 579c2cb2ca
commit a7bcfec453
12 changed files with 1347 additions and 832 deletions

View File

@ -0,0 +1,55 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/HuckRidgeSW/hvue"
)
type CompEthernetAddressesData2 struct {
*js.Object
}
func newCompEthernetAddressesData2(vm *hvue.VM) interface{} {
cc := &CompEthernetAddressesData2{
Object: js.Global.Get("Object").New(),
}
return cc
}
func InitCompEthernetAddresses2() {
/*
o := vue.NewOption()
o.Name = "EthernetAddresses"
o.SetDataWithMethods(newCompEthernetAddressesData2)
o.Template = compEthernetAddressesTemplate2
o.AddProp("settings")
*/
hvue.NewComponent(
"ethernet-addresses",
hvue.Template(compEthernetAddressesTemplate2),
hvue.DataFunc(newCompEthernetAddressesData2),
hvue.PropObj("settings", hvue.Types(hvue.PObject)),
)
}
const (
compEthernetAddressesTemplate2 = `
<div>
<table>
<tr>
<td>Host MAC address</td><td><input v-bind:value="settings.HostAddr" v-on:input="$emit('hostAddrChange', $event.target.value)"></td>
</tr>
<tr>
<td>Device MAC address</td><td><input v-bind:value="settings.DevAddr" v-on:input="$emit('devAddrChange', $event.target.value)"></td>
</tr>
</table>
</div>
`
)

64
web_client/hvueCompTab.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/HuckRidgeSW/hvue"
)
type CompTabData struct {
*js.Object
Id int `js:"id"`
IsActive bool `js:"isActive"`
}
func NewCompTabData(vm *hvue.VM) interface{} {
cc := &CompTabData{
Object: js.Global.Get("Object").New(),
}
cc.Id = -1
cc.IsActive = false
return cc
}
func InitCompTab() {
hvue.NewComponent(
"tab",
hvue.Template(compTabTemplate),
hvue.DataFunc(NewCompTabData),
hvue.PropObj("header", hvue.Types(hvue.PString), hvue.Default("tab name"), hvue.Required),
hvue.PropObj("disabled", hvue.Types(hvue.PBoolean), hvue.Default(false)),
hvue.PropObj("selected", hvue.Types(hvue.PBoolean), hvue.Default(false)),
/*
hvue.Computed("active", func(vm *hvue.VM) interface{} {
return true
}),
*/
hvue.Computed("_isTab", func(vm *hvue.VM) interface{} {
return true
}),
hvue.Computed("index", func(vm *hvue.VM) interface{} {
return 0
}),
hvue.Mounted(func(vm *hvue.VM) {
vm.Set("isActive", vm.Get("selected")) //propagate "selected" property over to "isActive" from data
}),
)
//return o.NewComponent()
}
const (
compTabTemplate = `
<div v-if="isActive">
<slot></slot>
</div>
`
)

View File

@ -0,0 +1,86 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/HuckRidgeSW/hvue"
)
type CompTabsData struct {
*js.Object
headers []string `js:"headers"`
tabs []*js.Object `js:"tabs"`
}
func NewCompTabsData(vm *hvue.VM) interface{} {
cc := &CompTabsData{
Object: js.Global.Get("Object").New(),
}
cc.headers = []string{}
cc.tabs = []*js.Object{}
return cc
}
func initTabs(vm *hvue.VM) {
// ToDo: clear children to allow dynamic adding/removing of tabs
id := 0
for _,child := range vm.Children {
isTab := child.Get("_isTab").Bool()
//println(child)
//println(isTab)
if isTab {
vm.Data.Get("tabs").Call("push", child)
child.Set("id", id)
id++
}
}
//ToDo: remove export (Debug only)
js.Global.Set("vm", vm)
}
func (c *CompTabsData) UpdateSelectedTab(vm *hvue.VM, selectedID int) {
println("Update selected ID: ", selectedID)
for _,child := range vm.Children {
isTab := child.Get("_isTab").Bool()
if isTab {
child.Set("isActive", child.Get("id").Int() == selectedID) //child.isActive = (selectedID == child.id)
}
}
}
func InitCompTabs() {
hvue.NewComponent(
"tabs",
hvue.DataFunc(NewCompTabsData),
hvue.Template(compTabsTemplate),
hvue.Mounted(initTabs),
hvue.MethodsOf(&CompTabsData{}),
)
}
const (
compTabsTemplate = `
<div>
<div>
<ul class="nav nav-tabs">
<li v-for="t in tabs" :class="{ 'active' : t.isActive }">
<a href="#" @click="UpdateSelectedTab(t.id)">{{t.id}}: {{ t.header }}</slot></a>
</li>
</ul>
</div>
<div class="tab-content">
<slot></slot>
</div>
</div>
`
)

View File

@ -0,0 +1,39 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/HuckRidgeSW/hvue"
)
type CompToggleSwitchData struct {
*js.Object
}
func newCompToggleSwitchData(vm *hvue.VM) interface{} {
newVM := &CompToggleSwitchData{
Object: js.Global.Get("Object").New(),
}
return newVM
}
func InitCompToggleSwitch() {
hvue.NewComponent(
"toggle-switch",
hvue.Template(compToggleSwitchTemplate),
hvue.DataFunc(newCompToggleSwitchData),
hvue.PropObj("value", hvue.Types(hvue.PBoolean), hvue.Required),
)
}
const (
compToggleSwitchTemplate = `
<label class="toggle-switch">
<input type="checkbox" v-bind:checked="value" v-on:change="$emit('input', $event.target.checked)">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
`
)

View File

@ -3,10 +3,10 @@ package main
import (
"github.com/gopherjs/gopherjs/js"
pb "../proto/gopherjs"
"fmt"
"time"
"context"
"google.golang.org/grpc/status"
"github.com/HuckRidgeSW/hvue"
)
@ -74,6 +74,8 @@ func (vGS VGadgetSettings) toGS() (gs *pb.GadgetSettings) {
}
func (vGS *VGadgetSettings) fromGS(gs *pb.GadgetSettings) {
println(gs)
vGS.Enabled = gs.Enabled
vGS.Vid = gs.Vid
vGS.Pid = gs.Pid
@ -114,14 +116,17 @@ func (vGS *VGadgetSettings) fromGS(gs *pb.GadgetSettings) {
}
// Note: internalize wouldn't work on this, as the nested structs don't translate back
type Com struct {
type CompUSBSettingsData struct {
*js.Object
GadgetSettings *VGadgetSettings `js:"gadgetSettings"`
DeployPending bool `js:"deployPending"`
CdcEcmDetails bool `js:"cdcEcmDetails"`
RndisDetails bool `js:"rndisDetails"`
}
func (c *Com) UodateToDeployedGadgetSettings() {
func (c *CompUSBSettingsData) UpdateToDeployedGadgetSettings(vm *hvue.VM) {
//gs := vue.GetVM(c).Get("gadgetSettings")
println("Trying to fetch deployed GadgetSettings")
@ -129,9 +134,8 @@ func (c *Com) UodateToDeployedGadgetSettings() {
ctx,cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
fmt.Printf("Before client + request\n")
deployedGs, err := pb.NewP4WNP1Client(serverAddr).GetDeployedGadgetSetting(ctx, &pb.Empty{})
if err != nil { fmt.Println(err); return }
if err != nil { println(err); return }
newGs := &VGadgetSettings{
Object: js.Global.Get("Object").New(),
@ -141,14 +145,13 @@ func (c *Com) UodateToDeployedGadgetSettings() {
}()
}
func (c *Com) ApplyGadgetSettings() {
//gs := vue.GetVM(c).Get("gadgetSettings")
println("Trying to deploy GadgetSettings: " + fmt.Sprintf("%+v",c.GadgetSettings.toGS()))
func (c *CompUSBSettingsData) ApplyGadgetSettings(vm *hvue.VM) {
//println("Trying to deploy GadgetSettings: " + fmt.Sprintf("%+v",c.GadgetSettings.toGS()))
println("Trying to deploy GadgetSettings...")
gs:=c.GadgetSettings.toGS()
go func() {
//ToDo: set apply button to inactive
//ToDo: defer set apply button to active
c.DeployPending = true
defer func() {c.DeployPending = false}()
ctx,cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
@ -156,14 +159,14 @@ func (c *Com) ApplyGadgetSettings() {
client := pb.NewP4WNP1Client(serverAddr)
//Set gadget settings
settedGs, err := client.SetGadgetSettings(ctx, gs)
_, err := client.SetGadgetSettings(ctx, gs)
if err != nil {
js.Global.Call("alert", "Error setting given gadget settings: " + status.Convert(err).Message())
fmt.Println(err)
c.UodateToDeployedGadgetSettings()
println(err)
c.UpdateToDeployedGadgetSettings(vm)
return
}
println(fmt.Sprintf("The following GadgetSettings have been set: %+v", settedGs))
println("New GadgetSettings have been set")
@ -171,11 +174,11 @@ func (c *Com) ApplyGadgetSettings() {
deployedGs,err := client.DeployGadgetSetting(ctx, &pb.Empty{})
if err != nil {
js.Global.Call("alert", "Error deploying gadget settings: " + status.Convert(err).Message())
fmt.Println(err)
c.UodateToDeployedGadgetSettings()
println(err)
c.UpdateToDeployedGadgetSettings(vm)
return
}
println(fmt.Sprintf("The following GadgetSettings have been deployed: %+v", deployedGs))
println("New GadgetSettings have been deployed")
js.Global.Call("alert", "New USB gadget settings deployed ")
@ -188,51 +191,48 @@ func (c *Com) ApplyGadgetSettings() {
}
func InitCompUSBSettings() {
hvue.NewComponent(
"usb-settings",
hvue.Template(compUSBSettingsTemplate),
hvue.DataFunc(newCompUSBSettingsData),
hvue.MethodsOf(&CompUSBSettingsData{}),
)
}
func New() interface{} {
func newCompUSBSettingsData(vm *hvue.VM) interface{} {
cc := &Com{
cc := &CompUSBSettingsData{
Object: js.Global.Get("Object").New(),
}
cc.GadgetSettings = &VGadgetSettings{
Object: js.Global.Get("Object").New(),
}
cc.GadgetSettings.fromGS(&pb.GadgetSettings{}) //start with empty settings, but create nested structs
cc.UodateToDeployedGadgetSettings()
fmt.Printf("Client: %+v\n", Client)
fmt.Printf("cc.gadgetSettings: %+v\n", cc.GadgetSettings)
fmt.Printf("GS.Vid: %+v\n", GS.Vid)
fmt.Printf("cc.gadgetSettings.Vid: %+v\n", cc.GadgetSettings.Vid)
cc.UpdateToDeployedGadgetSettings(vm)
cc.DeployPending = false
cc.RndisDetails = false
cc.CdcEcmDetails = false
return cc
}
type controller struct {
*js.Object
}
const (
template = `
compUSBSettingsTemplate = `
<div>
<table>
<tr> <td>USB gadget settings</td><td><button @click="ApplyGadgetSettings">Apply</button></td> </tr>
<tr> <td>USB gadget settings</td><td><button @click="ApplyGadgetSettings" :disabled="deployPending">Apply</button></td> </tr>
<tr>
<td>Gadget enabled</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Enabled">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Enabled"></toggle-switch>
</td>
</tr>
@ -246,88 +246,71 @@ const (
<tr>
<td>CDC ECM</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_CDC_ECM">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_CDC_ECM"></toggle-switch>
</td>
</tr>
<tr v-if="gadgetSettings.Use_CDC_ECM">
<td></td>
<td>
<ethernet-addresses v-bind:settings="gadgetSettings.CdcEcmSettings" @hostAddrChange="gadgetSettings.CdcEcmSettings.HostAddr=$event" @devAddrChange="gadgetSettings.CdcEcmSettings.DevAddr=$event"></ethernet-addresses>
</td>
</tr>
<tr>
<td>RNDIS</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_RNDIS">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_RNDIS"></toggle-switch>
<td></td>
<input type="checkbox" v-if="gadgetSettings.Use_RNDIS" v-model="rndisDetails">
</td>
</tr>
<tr v-if="rndisDetails">
<td></td>
<td>
<ethernet-addresses v-bind:settings="gadgetSettings.RndisSettings" @hostAddrChange="gadgetSettings.RndisSettings.HostAddr=$event" @devAddrChange="gadgetSettings.RndisSettings.DevAddr=$event"></ethernet-addresses>
</td>
</tr>
<tr>
<td>HID Keyboard</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_KEYBOARD">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_HID_KEYBOARD"></toggle-switch>
</td>
</tr>
<tr>
<td>HID Mouse</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_MOUSE">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_HID_MOUSE"></toggle-switch>
</td>
</tr>
<tr>
<td>HID Raw</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_RAW">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_HID_RAW"></toggle-switch>
</td>
</tr>
<tr>
<td>Serial</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_SERIAL">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_SERIAL"></toggle-switch>
</td>
</tr>
<tr>
<td>Mass Storage</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_UMS">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
<toggle-switch v-model="gadgetSettings.Use_UMS"></toggle-switch>
</td>
</tr>
</table>
</div>
`
)

View File

@ -2,15 +2,21 @@
<head>
<title>P4wnP1 by MaMe82</title>
<link rel="stylesheet" type="text/css" href="p4wnp1.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h1>P4wnP1 service - USB settings</h1>
<p>See Javascript console for details, entry script is webapp.js (generated by gopherjs)</p>
<div id="app">
<usb-settings></usb-settings>
<tabs>
<tab header="USB" :selected="true">
<usb-settings></usb-settings>
</tab>
<tab header="HID Script">
<p>Some bla on second</p>
</tab>
<tab header="foo" >blaaaa </tab>
<tab header="bar">blaaaa </tab>
</tabs>
</div>
<script type="text/javascript" src="webapp.js"></script>

View File

@ -7,9 +7,9 @@ import (
"time"
pb "../proto/gopherjs"
dom "honnef.co/go/js/dom"
"github.com/oskca/gopherjs-vue"
"honnef.co/go/js/dom"
"github.com/gopherjs/gopherjs/js"
"github.com/HuckRidgeSW/hvue"
)
var (
@ -32,6 +32,10 @@ func GetBaseURL() string {
return url
}
type appController struct {
*js.Object
}
func main() {
println(GetBaseURL())
@ -52,8 +56,23 @@ func main() {
}
vue.NewComponent(New, template).Register("usb-settings")
vm := vue.New("#app", new(controller))
/*
InitCompTabs().Register("tabs")
InitCompTab().Register("tab")
*/
InitCompEthernetAddresses2()
InitCompToggleSwitch()
InitCompUSBSettings()
InitCompTab()
InitCompTabs()
hvue.NewVM(hvue.El("#app"))
/*
vm := vue.New("#app", new(appController))
js.Global.Set("vm", vm)
println("vm:", vm)
*/
}

View File

@ -1,15 +1,47 @@
/*
<label class="toggle-switch">
<input type="checkbox">
<div>
<span class="on">On</span>
<span class="off">Off</span>
</div>
<span class="toggle-switch-slider"></span>
</label>
*/
html {
height: 100%;
}
body {
font-family: "Open Sans", sans-serif;
font-size: 1em;
line-height: 1.6;
background: linear-gradient(45deg, #9e9e9e, #c4c4c4 66%, #9e9e9e);
}
input {
/*border-radius: 99999px;*/
background-color: #707070;
background: linear-gradient(to top, #9e9e9e, #f4f4f4);
height: 30px;
box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, 0.7);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
font-size: 0.9em;
font-weight: 600;
}
button {
border-radius: 99999px;
background-color: #707070;
background: linear-gradient(to bottom, #f4f4f4, #9e9e9e);
height: 30px;
width: 100px;
text-transform: uppercase;
font-size: 0.9em;
font-weight: 800;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
letter-spacing: 1px;
}
.toggle-switch {
position: relative;
@ -20,7 +52,8 @@
background: linear-gradient(to top, #f4f4f4, #9e9e9e 20%);
border-radius: 99999px;
/* box-shadow: h-offset v-offset blur spread color inset initial inherit */
box-shadow: 0 2px 0 0 #FFFFFF, 0 -2px 0 0 #f4f4f4; /* 2px offset gray to top, white to bottom */
box-shadow: 1px 1px 1px 0 #3f3f3f, -1px -1px 1px 0 #ffffff; /* 2px offset gray to top, white to bottom */
cursor: pointer;
}
@ -134,7 +167,7 @@
.toggle-switch input[type="checkbox"]:checked ~ div > .on {
color: rgb(0,154,0);
text-shadow: 0px 1px 0px rgba(27,76,37, 0.5);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}
.off {
@ -148,3 +181,57 @@
}
/* NAV BAR */
.nav {
padding-left: 0;
margin-bottom: 0;
list-style: none;
}
.nav::after, .nav::before {
display: table;
content: " ";
clear: both;
}
.nav-tabs {
margin-bottom: 15px;
}
.nav-tabs::after {
content:"";
width: 100%;
height: 2px;
background: linear-gradient(to right, #f4f4f4 20%, #3f3f3f00);
clear:both;
}
.nav-tabs > li {
float: left;
margin-bottom: -2px;
}
.nav > li {
position: relative;
display: block;
}
.nav > li > a {
position: relative;
display: block;
padding: 10px 15px;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover {
/* color: #555; */
cursor: default;
background: linear-gradient(to bottom, #f4f4f4, #9e9e9e);
box-shadow: inset 1px 1px 1px 0 #ffffff,inset -1px 1px 1px 0 #3f3f3f;
border-bottom-width: 0;
}
.nav-tabs > li > a {
margin-right: 2px;
line-height: 1.42857143;
border-radius: 15px 15px 0 0;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
box-shadow: inset 1px 1px 1px 0 #3f3f3f,inset -1px 1px 1px 0 #ffffff;
color: black;
text-transform: uppercase;
font-size: 0.9em;
font-weight: 800;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
text-decoration: none;
}

View File

@ -2,15 +2,21 @@
<head>
<title>P4wnP1 by MaMe82</title>
<link rel="stylesheet" type="text/css" href="p4wnp1.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<h1>P4wnP1 service - USB settings</h1>
<p>See Javascript console for details, entry script is webapp.js (generated by gopherjs)</p>
<div id="app">
<usb-settings></usb-settings>
<tabs>
<tab header="USB" :selected="true">
<usb-settings></usb-settings>
</tab>
<tab header="HID Script">
<p>Some bla on second</p>
</tab>
<tab header="foo" >blaaaa </tab>
<tab header="bar">blaaaa </tab>
</tabs>
</div>
<script type="text/javascript" src="webapp.js"></script>

View File

@ -1,14 +1,3 @@
/*
<label class="toggle-switch">
<input type="checkbox">
<div>
<span class="on">On</span>
<span class="off">Off</span>
</div>
<span class="toggle-switch-slider"></span>
</label>
*/
html {
height: 100%;
@ -37,7 +26,7 @@ input {
button {
border-radius: 99999px;
background-color: #707070;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
background: linear-gradient(to bottom, #f4f4f4, #9e9e9e);
height: 30px;
width: 100px;
@ -192,3 +181,57 @@ button {
}
/* NAV BAR */
.nav {
padding-left: 0;
margin-bottom: 0;
list-style: none;
}
.nav::after, .nav::before {
display: table;
content: " ";
clear: both;
}
.nav-tabs {
margin-bottom: 15px;
}
.nav-tabs::after {
content:"";
width: 100%;
height: 2px;
background: linear-gradient(to right, #f4f4f4 20%, #3f3f3f00);
clear:both;
}
.nav-tabs > li {
float: left;
margin-bottom: -2px;
}
.nav > li {
position: relative;
display: block;
}
.nav > li > a {
position: relative;
display: block;
padding: 10px 15px;
}
.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover {
/* color: #555; */
cursor: default;
background: linear-gradient(to bottom, #f4f4f4, #9e9e9e);
box-shadow: inset 1px 1px 1px 0 #ffffff,inset -1px 1px 1px 0 #3f3f3f;
border-bottom-width: 0;
}
.nav-tabs > li > a {
margin-right: 2px;
line-height: 1.42857143;
border-radius: 15px 15px 0 0;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
box-shadow: inset 1px 1px 1px 0 #3f3f3f,inset -1px 1px 1px 0 #ffffff;
color: black;
text-transform: uppercase;
font-size: 0.9em;
font-weight: 800;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
text-decoration: none;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long