Skip to content
Open
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
11 changes: 11 additions & 0 deletions pkg/app/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Composer interface {
// Render constructs and returns the visual representation of the component
// as a node tree.
Render() UI
GetLazyCSSPath() string
SetLazyCSSPath(path string) Composer

setRef(Composer) Composer
depth() uint
Expand Down Expand Up @@ -165,6 +167,7 @@ type Compo struct {
ref Composer
parentElement UI
rootElement UI
lazyCssPath string
}

// JSValue retrieves the JavaScript value associated with the component's root.
Expand Down Expand Up @@ -204,6 +207,14 @@ func (c *Compo) Render() UI {
)
}

func (c *Compo) SetLazyCSSPath(path string) Composer {
c.lazyCssPath = path
return c.ref
}
func (c *Compo) GetLazyCSSPath() string {
return c.lazyCssPath
}

// ValueTo captures the value of the DOM element (if it exists) that triggered
// an event, and assigns it to the provided receiver. The receiver must be a
// pointer pointing to either a string, integer, unsigned integer, or a float.
Expand Down
49 changes: 49 additions & 0 deletions pkg/app/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -643,6 +644,11 @@ func (h *Handler) servePage(w http.ResponseWriter, r *http.Request) {
icon = h.Icon.Default
}

routeComposer := routes.routes[page.URL().Path]
var components []Composer
components = getAllChildCompnents(components, routeComposer())
var addedComponentLinks = make(map[string]string)

var b bytes.Buffer
err := engine.Encode(&b, h.HTML().
Lang(page.Lang()).
Expand Down Expand Up @@ -765,6 +771,23 @@ func (h *Handler) servePage(w http.ResponseWriter, r *http.Request) {
Range(h.RawHeaders).Slice(func(i int) UI {
return Raw(h.RawHeaders[i])
}),
Range(components).Slice(func(i int) UI {
componentName := strings.ToLower(reflect.TypeOf(components[i]).Elem().Name())
linkHref := fmt.Sprintf("lazy-css-%s", componentName)
_, isAdded := addedComponentLinks[componentName]

if !isAdded {
if resource := parseHTTPResource(components[i].GetLazyCSSPath()); resource.URL != "" {
addedComponentLinks[componentName] = linkHref
return resource.toLink().
Type("text/css").
Rel("stylesheet").
ID(linkHref)
}
return nil
}
return nil
}),
),
h.Body().privateBody(
Aside().
Expand Down Expand Up @@ -794,6 +817,32 @@ func (h *Handler) servePage(w http.ResponseWriter, r *http.Request) {
w.Write(b.Bytes())
}

func getAllChildCompnents(components []Composer, v UI) []Composer {

var children []UI
switch n := v.(type) {
case Composer:
children = FilterUIElems(n.Render())
if n.GetLazyCSSPath() != "" {
components = append(components, n)
}
case HTML:
children = FilterUIElems(n.body()...)
default:
return components
}

if len(children) == 0 {
return components
}

for _, c := range children {
components = getAllChildCompnents(components, c)
}

return components
}

func (h *Handler) serveLibrary(w http.ResponseWriter, r *http.Request, library []byte) {
w.Header().Set("Content-Length", strconv.Itoa(len(library)))
w.Header().Set("Content-Type", "text/css")
Expand Down
38 changes: 38 additions & 0 deletions pkg/app/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package app

import (
"bytes"
"fmt"
"html"
"io"
"reflect"
"strconv"
"strings"
"time"

"github.com/maxence-charriere/go-app/v10/pkg/errors"
Expand Down Expand Up @@ -237,6 +239,10 @@ func (m nodeManager) mountComponent(ctx Context, depth uint, v Composer) (UI, er
ctx.Dispatch(mounter.OnMount)
}

if !IsServer {
m.loadLazyCSS(v)
}

root, err := m.renderComponent(v)
if err != nil {
return nil, errors.New("rendering component failed").
Expand All @@ -256,6 +262,36 @@ func (m nodeManager) mountComponent(ctx Context, depth uint, v Composer) (UI, er
return v, nil
}

func (m nodeManager) loadLazyCSS(v Composer) {
doc := Window().Get("document")
existing := doc.Call("querySelector", fmt.Sprintf("link[href='%s']", v.GetLazyCSSPath()))
if v.GetLazyCSSPath() != "" && !IsServer && existing.IsNull() {
componentName := strings.ToLower(reflect.TypeOf(v).Elem().Name())
done := make(chan struct{})
css, _ := Window().createElement("link", "")
css.setAttr("rel", "stylesheet")
css.setAttr("type", "text/css")
css.setAttr("id", fmt.Sprintf("lazy-css-%s", componentName))
css.setAttr("href", v.GetLazyCSSPath())
onload := FuncOf(func(this Value, args []Value) interface{} {
close(done)
return nil
})
css.Set("onload", onload)
css.Set("onerror", onload)
Window().Get("document").Get("head").appendChild(css)
<-done
}
}

func (m nodeManager) unloadLazyCss(v Composer) {
doc := Window().Get("document")
existing := doc.Call("querySelector", fmt.Sprintf("link[href='%s']", v.GetLazyCSSPath()))
if !existing.IsNull() {
Window().Get("document").Get("head").removeChild(existing)
}
}

func (m nodeManager) renderComponent(v Composer) (UI, error) {
rendering := FilterUIElems(v.Render())
if len(rendering) == 0 {
Expand Down Expand Up @@ -318,6 +354,8 @@ func (m nodeManager) dismountComponent(v Composer) {
m.Dismount(v.root())
v.setRef(nil)

m.unloadLazyCss(v)

if dismounter, ok := v.(Dismounter); ok {
dismounter.OnDismount()
}
Expand Down