Documentation ¶
Index ¶
- Constants
- Variables
- func AttrMapFromNode(node *html.Node) map[string]string
- func GenerateRandomString(n int) (string, error)
- func LiveErrorMap() map[string]string
- type BrowserEvent
- type ChildLiveComponent
- type ComponentContext
- type ComponentLifeCycle
- type ComponentLifeTime
- type ComponentLifeTimeMessage
- type DOMEvent
- type DiffType
- type EventSource
- type EventSourceType
- type HTTPHandlerCtx
- type HTTPMiddleware
- type LifeTimeStage
- type LiveComponent
- func (l *LiveComponent) Create(life *ComponentLifeCycle) error
- func (l *LiveComponent) GetFieldFromPath(path string) *reflect.Value
- func (l *LiveComponent) InvokeMethodInPath(path string, data map[string]string, domEvent *DOMEvent) error
- func (l *LiveComponent) Kill() error
- func (l *LiveComponent) KillChildren()
- func (l *LiveComponent) LiveRender() (*diff, error)
- func (l *LiveComponent) Mount() error
- func (l *LiveComponent) MountChildren() error
- func (l *LiveComponent) Render() (string, error)
- func (l *LiveComponent) RenderChild(fn reflect.Value, _ ...reflect.Value) template.HTML
- func (l *LiveComponent) SetValueInPath(value string, path string) error
- func (l *LiveComponent) Update()
- func (l *LiveComponent) UpdateWithSource(source *EventSource)
- type LiveComponentWrapper
- func (l *LiveComponentWrapper) BeforeMount(_ *LiveComponent)
- func (l *LiveComponentWrapper) BeforeUnmount(_ *LiveComponent)
- func (l *LiveComponentWrapper) Commit()
- func (l *LiveComponentWrapper) Create(lc *LiveComponent)
- func (l *LiveComponentWrapper) Mounted(_ *LiveComponent)
- func (l *LiveComponentWrapper) TemplateHandler(_ *LiveComponent) string
- type LiveEventsChannel
- type LivePageEvent
- type LiveRenderer
- type LiveResponse
- type LiveServer
- func (s *LiveServer) CreateHTMLHandler(f func() *LiveComponent, c PageContent) func(ctx *fiber.Ctx) error
- func (s *LiveServer) CreateHTMLHandlerWithMiddleware(f func(ctx context.Context) *LiveComponent, content PageContent, ...) func(c *fiber.Ctx) error
- func (s *LiveServer) HandleFirstRequest(lc *LiveComponent, c PageContent) (*LiveResponse, error)
- func (s *LiveServer) HandleHTMLRequest(ctx *fiber.Ctx, lc *LiveComponent, c PageContent)
- func (s *LiveServer) HandleWSRequest(c *websocket.Conn)
- type LiveState
- type LiveWire
- type Log
- type LoggerBasic
- type Page
- type PageContent
- type PageEnum
- type PatchBrowser
- type PatchInstruction
- type PatchNodeChildren
- type PatchTreeNode
- type Random
- type Session
- type SessionStatus
- type WireSessions
Constants ¶
const ( LogTrace = iota - 1 LogDebug LogInfo LogWarn LogError LogFatal LogPanic )
const ( EventLiveInput = "li" EventLiveMethod = "lm" EventLiveDom = "ld" EventLiveDisconnect = "lx" EventLiveError = "le" EventLiveConnectElement = "lce" )
const ComponentIdAttrKey = "go-live-component-id"
const (
EventSourceInput = "input"
)
const PageComponentMounted = 2
const PageComponentUpdated = 1
Variables ¶
var ( ErrComponentNotPrepared = errors.New("Component need to be prepared") ErrComponentWithoutLog = errors.New("Component without log defined") ErrComponentNil = errors.New("Component nil") )
var ( ErrCouldNotProvideValidSelector = fmt.Errorf("could not provide a valid selector") ErrElementNotSigned = fmt.Errorf("element is not signed with go-live-uid") )
var BasePage *template.Template
var BasePageString = `<!DOCTYPE html>
<html lang="{{ .Lang }}">
<head>
<meta charset="UTF-8" />
<title>{{ .Title }}</title>
{{ .Head }}
</head>
<body>
{{ .Body }}
</body>
<script type="application/javascript">
const GO_LIVE_CONNECTED="go-live-connected",GO_LIVE_COMPONENT_ID="go-live-component-id",EVENT_LIVE_DOM_COMPONENT_ID_KEY="cid",EVENT_LIVE_DOM_INSTRUCTIONS_KEY="i",EVENT_LIVE_DOM_TYPE_KEY="t",EVENT_LIVE_DOM_CONTENT_KEY="c",EVENT_LIVE_DOM_ATTR_KEY="a",EVENT_LIVE_DOM_SELECTOR_KEY="s",EVENT_LIVE_DOM_INDEX_KEY="i",handleChange={"{{ .Enum.DiffSetAttr }}":handleDiffSetAttr,"{{ .Enum.DiffRemoveAttr }}":handleDiffRemoveAttr,"{{ .Enum.DiffReplace }}":handleDiffReplace,"{{ .Enum.DiffRemove }}":handleDiffRemove,"{{ .Enum.DiffSetInnerHTML }}":handleDiffSetInnerHTML,"{{ .Enum.DiffAppend }}":handleDiffAppend,"{{ .Enum.DiffMove }}":handleDiffMove},goLive={server:createConnection(),handlers:[],once:createOnceEmitter(),getLiveComponent(a){return document.querySelector(["*[",GO_LIVE_COMPONENT_ID,"=",a,"]"].join(""))},on(a,b){const c=this.handlers.push({name:a,handler:b});return c-1},findHandler(a){return this.handlers.filter(b=>b.name===a)},emit(a,b){for(const c of this.findHandler(a))c.handler(b)},off(a){this.handlers.splice(a,1)},send(a){goLive.server.send(JSON.stringify(a))},connectChildren(a){const b=a.querySelectorAll("*["+GO_LIVE_COMPONENT_ID+"]");b.forEach(a=>{this.connectElement(a)})},connectElement(a){if(typeof a=="string"){console.warn("is string");return}if(!isElement(a)){console.warn("not element");return}const b=[],c=findLiveClicksFromElement(a);c.forEach(function(a){const c=getComponentIdFromElement(a);a.addEventListener("click",function(b){goLive.send({name:"{{ .Enum.EventLiveMethod }}",component_id:c,method_name:a.getAttribute("go-live-click"),method_data:dataFromElementAttributes(a)})}),b.push(a)});const d=findLiveKeyDownFromElement(a);d.forEach(function(a){const e=getComponentIdFromElement(a),f=a.getAttribute("go-live-keydown"),c=a.attributes;let d=[];for(let a=0;a<c.length;a++)(c[a].name==="go-live-key"||c[a].name.startsWith("go-live-key-"))&&d.push(c[a].value);a.addEventListener("keydown",function(g){const c=String(g.code);let b=!0;if(d.length!==0){b=!1;for(let a=0;a<d.length;a++)if(d[a]===c){b=!0;break}}b&&goLive.send({name:"{{ .Enum.EventLiveMethod }}",component_id:e,method_name:f,method_data:dataFromElementAttributes(a),dom_event:{keyCode:c}})}),b.push(a)});const e=findLiveInputsFromElement(a);e.forEach(function(a){const c=a.getAttribute("type"),d=getComponentIdFromElement(a);a.addEventListener("input",function(e){let b=a.value;c==="checkbox"&&(b=a.checked),goLive.send({name:"{{ .Enum.EventLiveInput }}",component_id:d,key:a.getAttribute("go-live-input"),value:String(b)})}),b.push(a)});for(const a of b)a.setAttribute(GO_LIVE_CONNECTED,!0)},connect(a){const b=goLive.getLiveComponent(a);goLive.connectElement(b),goLive.on("{{ .Enum.EventLiveDom }}",function(b){if(a===b[EVENT_LIVE_DOM_COMPONENT_ID_KEY])for(const c of b[EVENT_LIVE_DOM_INSTRUCTIONS_KEY]){const f=c[EVENT_LIVE_DOM_TYPE_KEY],g=c[EVENT_LIVE_DOM_CONTENT_KEY],h=c[EVENT_LIVE_DOM_ATTR_KEY],d=c[EVENT_LIVE_DOM_SELECTOR_KEY],i=c[EVENT_LIVE_DOM_INDEX_KEY],e=document.querySelector(d);if(!e){console.error("Element not found",d);return}handleChange[f]({content:g,attr:h,index:i},e,a)}})}};goLive.once.on("WS_CONNECTION_OPEN",()=>{goLive.on("{{ .Enum.EventLiveConnectElement }}",a=>{const b=a[EVENT_LIVE_DOM_COMPONENT_ID_KEY];goLive.connect(b)}),goLive.on("{{ .Enum.EventLiveError }}",a=>{console.error("message",a.m),a.m==='{{ index .EnumLiveError ` + "`LiveErrorSessionNotFound`" + `}}'&&window.location.reload(!1)})}),goLive.server.onmessage=a=>{try{const b=JSON.parse(a.data);goLive.emit(b.t,b)}catch(b){console.log("Error",b),console.log("Error message",a.data)}},goLive.server.onopen=()=>{goLive.once.emit("WS_CONNECTION_OPEN")};function createConnection(){const a=[];return window.location.protocol==="https:"?a.push("wss"):a.push("ws"),a.push("://",window.location.host,"/ws"),new WebSocket(a.join(""))}function createOnceEmitter(){const a={},b=(b,c)=>(a[b]={called:c,cbs:[]},a[b]);return{on(d,e){let c=a[d];c||(c=b(d,!1)),c.cbs.push(e)},emit(c,...e){const d=a[c];if(!d){b(c,!0);return}for(const a of d.cbs)a()}}}const findLiveInputsFromElement=a=>a.querySelectorAll(["*[go-live-input]:not([",GO_LIVE_CONNECTED,"])"].join("")),findLiveClicksFromElement=a=>a.querySelectorAll(["*[go-live-click]:not([",GO_LIVE_CONNECTED,"])"].join("")),findLiveKeyDownFromElement=a=>a.querySelectorAll(["*[go-live-keydown]:not([",GO_LIVE_CONNECTED,"])"].join("")),dataFromElementAttributes=c=>{const a=c.attributes;let b={};for(let c=0;c<a.length;c++)a[c].name.startsWith("go-live-data-")&&(b[a[c].name.substring(13)]=a[c].value);return b};function getElementChild(b,c){let a=b.firstElementChild;while(c>0){if(!a){console.error("Element not found in path",b);return}if(a=a.nextSibling,a.nodeType!==Node.ELEMENT_NODE)continue;c--}return a}function isElement(a){return typeof HTMLElement=="object"?a instanceof HTMLElement:a&&typeof a=="object"&&a.nodeType===1&&typeof a.nodeName=="string"}function handleDiffSetAttr(c,b){const{attr:a}=c;a.Name==="value"&&b.value?b.value=a.Value:b.setAttribute(a.Name,a.Value)}function handleDiffRemoveAttr(a,b){const{attr:c}=a;b.removeAttribute(c.Name)}function handleDiffReplace(d,a){const{content:e}=d,b=document.createElement("div");b.innerHTML=e;const c=a.parentElement;c.replaceChild(b.firstChild,a),goLive.connectElement(c)}function handleDiffRemove(c,a){const b=a.parentElement;b.removeChild(a)}function handleDiffSetInnerHTML(c,a){let{content:b}=c;if(b===void 0&&(b=""),a.nodeType===Node.TEXT_NODE){a.textContent=b;return}a.innerHTML=b,goLive.connectElement(a)}function handleDiffAppend(c,a){const{content:d}=c,b=document.createElement("div");b.innerHTML=d;const e=b.firstChild;a.appendChild(e),goLive.connectElement(a)}function handleDiffMove(c,a){const b=a.parentNode;b.removeChild(a);const d=getElementChild(b,c.index);b.replaceChild(a,d)}const getComponentIdFromElement=a=>{const b=a.getAttribute("go-live-component-id");return b?b:a.parentElement?getComponentIdFromElement(a.parentElement):void 0}
</script>
</html>
`
Code automatically generated. DO NOT EDIT. > go run ci/create_html_page.go
var (
LiveErrorSessionNotFound = "session_not_found"
)
Functions ¶
func AttrMapFromNode ¶
AttrMapFromNode todo
func GenerateRandomString ¶
func LiveErrorMap ¶
Types ¶
type BrowserEvent ¶
type ChildLiveComponent ¶
type ChildLiveComponent interface{}
type ComponentContext ¶ added in v0.0.2
type ComponentContext struct {
Pairs map[string]interface{}
}
func NewComponentContext ¶ added in v0.0.2
func NewComponentContext() ComponentContext
type ComponentLifeCycle ¶
type ComponentLifeCycle chan ComponentLifeTimeMessage
type ComponentLifeTime ¶
type ComponentLifeTime interface { Create(component *LiveComponent) TemplateHandler(component *LiveComponent) string Mounted(component *LiveComponent) BeforeMount(component *LiveComponent) BeforeUnmount(component *LiveComponent) }
type ComponentLifeTimeMessage ¶
type ComponentLifeTimeMessage struct { Stage LifeTimeStage Component *LiveComponent Source *EventSource }
type EventSource ¶
type EventSource struct { Type EventSourceType Value string }
type EventSourceType ¶
type EventSourceType string
type HTTPHandlerCtx ¶
HTTPHandlerCtx HTTP Handler with a page level context.
type HTTPMiddleware ¶
type HTTPMiddleware func(next HTTPHandlerCtx) HTTPHandlerCtx
HTTPMiddleware Middleware to run on HTTP requests.
type LifeTimeStage ¶
type LifeTimeStage int
const ( WillCreate LifeTimeStage = iota Created WillMount WillMountChildren ChildrenMounted Mounted Rendered Updated WillUnmount Unmounted )
type LiveComponent ¶
type LiveComponent struct { Name string IsMounted bool IsCreated bool Exited bool Context ComponentContext // contains filtered or unexported fields }
func NewLiveComponent ¶
func NewLiveComponent(name string, component ComponentLifeTime) *LiveComponent
NewLiveComponent ...
func (*LiveComponent) Create ¶
func (l *LiveComponent) Create(life *ComponentLifeCycle) error
func (*LiveComponent) GetFieldFromPath ¶
func (l *LiveComponent) GetFieldFromPath(path string) *reflect.Value
GetFieldFromPath ...
func (*LiveComponent) InvokeMethodInPath ¶
func (l *LiveComponent) InvokeMethodInPath(path string, data map[string]string, domEvent *DOMEvent) error
InvokeMethodInPath ...
func (*LiveComponent) KillChildren ¶
func (l *LiveComponent) KillChildren()
func (*LiveComponent) LiveRender ¶
func (l *LiveComponent) LiveRender() (*diff, error)
LiveRender render a new version of the Component, and detect differences from the last render and sets the "new old" version of render
func (*LiveComponent) Mount ¶
func (l *LiveComponent) Mount() error
Mount 2. the Component loading html
func (*LiveComponent) MountChildren ¶
func (l *LiveComponent) MountChildren() error
func (*LiveComponent) RenderChild ¶
func (*LiveComponent) SetValueInPath ¶
func (l *LiveComponent) SetValueInPath(value string, path string) error
SetValueInPath ...
func (*LiveComponent) Update ¶
func (l *LiveComponent) Update()
func (*LiveComponent) UpdateWithSource ¶
func (l *LiveComponent) UpdateWithSource(source *EventSource)
type LiveComponentWrapper ¶
type LiveComponentWrapper struct { Name string Component *LiveComponent }
LiveComponentWrapper is a struct
func (*LiveComponentWrapper) BeforeMount ¶
func (l *LiveComponentWrapper) BeforeMount(_ *LiveComponent)
BeforeMount the Component loading html
func (*LiveComponentWrapper) BeforeUnmount ¶
func (l *LiveComponentWrapper) BeforeUnmount(_ *LiveComponent)
BeforeUnmount before we kill the Component
func (*LiveComponentWrapper) Commit ¶
func (l *LiveComponentWrapper) Commit()
Commit puts an boolean to the commit channel and notifies who is listening
func (*LiveComponentWrapper) Create ¶
func (l *LiveComponentWrapper) Create(lc *LiveComponent)
func (*LiveComponentWrapper) Mounted ¶
func (l *LiveComponentWrapper) Mounted(_ *LiveComponent)
BeforeMount the Component loading html
func (*LiveComponentWrapper) TemplateHandler ¶
func (l *LiveComponentWrapper) TemplateHandler(_ *LiveComponent) string
TemplateHandler ...
type LiveEventsChannel ¶
type LiveEventsChannel chan LivePageEvent
type LivePageEvent ¶
type LivePageEvent struct { Type int Component *LiveComponent Source *EventSource }
type LiveRenderer ¶
type LiveRenderer struct {
// contains filtered or unexported fields
}
func (*LiveRenderer) LiveRender ¶
func (lr *LiveRenderer) LiveRender(data interface{}) (*diff, error)
type LiveResponse ¶
type LiveServer ¶
func NewServer ¶
func NewServer() *LiveServer
func (*LiveServer) CreateHTMLHandler ¶
func (s *LiveServer) CreateHTMLHandler(f func() *LiveComponent, c PageContent) func(ctx *fiber.Ctx) error
func (*LiveServer) CreateHTMLHandlerWithMiddleware ¶
func (s *LiveServer) CreateHTMLHandlerWithMiddleware(f func(ctx context.Context) *LiveComponent, content PageContent, middlewares ...HTTPMiddleware) func(c *fiber.Ctx) error
func (*LiveServer) HandleFirstRequest ¶
func (s *LiveServer) HandleFirstRequest(lc *LiveComponent, c PageContent) (*LiveResponse, error)
func (*LiveServer) HandleHTMLRequest ¶
func (s *LiveServer) HandleHTMLRequest(ctx *fiber.Ctx, lc *LiveComponent, c PageContent)
func (*LiveServer) HandleWSRequest ¶
func (s *LiveServer) HandleWSRequest(c *websocket.Conn)
type LiveWire ¶
type LiveWire struct {
Sessions WireSessions
}
func (*LiveWire) DeleteSession ¶
func (*LiveWire) GetSession ¶
type LoggerBasic ¶
func NewLoggerBasic ¶
func NewLoggerBasic() *LoggerBasic
type Page ¶
type Page struct { Events LiveEventsChannel ComponentsLifeCycle *ComponentLifeCycle // Components is a list that handle all the components from the page Components map[string]*LiveComponent // contains filtered or unexported fields }
func NewLivePage ¶
func NewLivePage(c *LiveComponent) *Page
func (*Page) Emit ¶
func (lp *Page) Emit(lts int, c *LiveComponent)
func (*Page) EmitWithSource ¶
func (lp *Page) EmitWithSource(lts int, c *LiveComponent, source *EventSource)
func (*Page) HandleBrowserEvent ¶
func (lp *Page) HandleBrowserEvent(m BrowserEvent) error
func (*Page) SetContent ¶
func (lp *Page) SetContent(c PageContent)
type PageContent ¶
type PatchBrowser ¶
type PatchBrowser struct { ComponentID string `json:"cid,omitempty"` Type string `json:"t"` Message string `json:"m"` Instructions []PatchInstruction `json:"i,omitempty"` }
func NewPatchBrowser ¶
func NewPatchBrowser(componentID string) *PatchBrowser
func (*PatchBrowser) AddInstruction ¶
func (pb *PatchBrowser) AddInstruction(pi PatchInstruction)
type PatchInstruction ¶
type PatchNodeChildren ¶
type PatchNodeChildren map[int]*PatchTreeNode
type PatchTreeNode ¶
type PatchTreeNode struct { Children PatchNodeChildren `json:"c,omitempty"` Instruction []PatchInstruction `json:"i"` }
type Random ¶
type Random struct {
// contains filtered or unexported fields
}
func (Random) GenerateSmall ¶
type Session ¶
type Session struct { LivePage *Page OutChannel chan PatchBrowser Status SessionStatus // contains filtered or unexported fields }
func NewSession ¶
func NewSession() *Session
func (*Session) ActivatePage ¶
func (*Session) IngestMessage ¶
func (s *Session) IngestMessage(message BrowserEvent) error
func (*Session) LiveRenderComponent ¶
func (s *Session) LiveRenderComponent(c *LiveComponent, source *EventSource) error
LiveRenderComponent render the updated Component and compare with last state. It may apply with *all child components*
func (*Session) QueueMessage ¶
func (s *Session) QueueMessage(message PatchBrowser)
type SessionStatus ¶ added in v0.0.2
type SessionStatus string
const ( SessionNew SessionStatus = "n" SessionOpen SessionStatus = "o" SessionClosed SessionStatus = "c" )