Documentation
¶
Overview ¶
Package jawstree provides a JaWS widget and embedded assets for the Quercus.js treeview library.
Example ¶
Example wires jawstree (and jawsboot) into an HTTP server. It is a compile-checked illustration only: it starts a blocking server, so it has no testable Output and is not executed by "go test".
package main
import (
"embed"
"log/slog"
"net/http"
"sync"
"github.com/linkdata/jaws"
"github.com/linkdata/jaws/jawsboot"
"github.com/linkdata/jaws/jawstree"
"github.com/linkdata/jaws/lib/bind"
"github.com/linkdata/jaws/lib/templatereloader"
"github.com/linkdata/jaws/lib/ui"
"github.com/linkdata/staticserve"
)
// This example assumes an 'assets' directory:
//
//. assets/
//. static/
//. images/
//. favicon.png
//. ui/
//. index.html
//go:embed assets
var assetsFS embed.FS
func setupJaws(jw *jaws.Jaws, mux *http.ServeMux) (err error) {
mux.Handle("GET /jaws/", jw) // Ensure the JaWS routes are handled
var tmpl jaws.TemplateLookuper
if tmpl, err = templatereloader.New(assetsFS, "assets/ui/*.html", ""); err == nil {
_ = jw.AddTemplateLookuper(tmpl)
// Initialize jawsboot; we will serve the JavaScript and CSS from /static/*.[js|css].
// All files under assets/static will be available under /static. Any favicon loaded
// this way will have its URL available using jaws.FaviconURL().
if err = jw.Setup(mux.Handle, "/static",
jawsboot.Setup,
jawstree.Setup,
staticserve.MustNewFS(assetsFS, "assets/static", "images/favicon.png"),
); err == nil {
// Add a route to our index template with a bound variable accessible as '.Dot' in the template
var mu sync.Mutex
var f float64
mux.Handle("GET /", ui.Handler(jw, "index.html", bind.New(&mu, &f)))
}
}
return
}
// Example wires jawstree (and jawsboot) into an HTTP server. It is a
// compile-checked illustration only: it starts a blocking server, so it has no
// testable Output and is not executed by "go test".
func main() {
jw, err := jaws.New()
if err == nil {
jw.Logger = slog.Default()
if err = setupJaws(jw, http.DefaultServeMux); err == nil {
// start the JaWS processing loop and the HTTP server
go jw.Serve()
slog.Error(http.ListenAndServe("localhost:8080", nil).Error())
}
}
if err != nil {
panic(err)
}
}
Output:
Index ¶
- func Setup(jw *jaws.Jaws, handleFn jaws.HandleFunc, prefix string) (urls []*url.URL, err error)
- type Node
- func (node *Node) GetNames() (names []string)
- func (node *Node) GetSelected() (nameLists [][]string)
- func (node *Node) HasNames(names []string) (yes bool)
- func (node *Node) JawsPathSet(elem *jaws.Element, jsPath string, value any)
- func (node *Node) JawsSetPath(elem *jaws.Element, jsPath string, value any) (err error)
- func (node *Node) MarshalJSON() (b []byte, err error)
- func (node *Node) SetSelected(nameLists [][]string) (changed []*Node)
- func (node *Node) Walk(jsPath string, fn func(jsPath string, node *Node))
- type Option
- type Tree
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Setup ¶
Setup registers embedded jawstree static assets under prefix.
It is intended to be passed to jaws.Jaws.Setup. Returned URLs should be included in the page head through jaws.Jaws.GenerateHeadHTML.
Types ¶
type Node ¶
type Node struct {
Tree *Tree `json:"-"` // owning tree, set by New
Parent *Node `json:"-"` // parent node, nil for root
Name string `json:"name"` // display name
ID string `json:"id,omitzero"` // JSON path ID, set by New
Selected bool `json:"selected,omitzero"` // selected state
Disabled bool `json:"disabled,omitzero"` // emitted as "selectable":false (inverted) on the wire
Children []*Node `json:"children,omitzero"` // child nodes
}
Node is one tree node rendered by Tree.
Concurrency: once the owning Tree has been rendered, its Node tree is shared with the JaWS event goroutines, which access it under the Tree's lock (the embedded ui.JsVar is an RWLocker). The exported Node accessors below are not internally synchronized, so callers must hold that lock when using them on a rendered Tree: the Tree's read lock (RLock) for the read-only helpers (Node.Walk, Node.HasNames, Node.GetNames, Node.GetSelected) and its write lock (Lock) for the mutating Node.SetSelected. No locking is needed before the Tree is rendered (for example while building it in New).
marshalJSON is the single source of truth for the wire shape sent to Quercus.js; MarshalJSON delegates to it, so the struct json tags below are not actually used for encoding and must be kept in sync with marshalJSON by hand.
func Root ¶
func Root(r *os.Root, filterFn func(dirpath string, ent fs.DirEntry) (include bool)) (rootnode *Node, err error)
Root builds a root node from an os.Root. If filterFn is not nil, it must return true for a directory entry to be included in the tree.
Building the tree is best-effort: if one or more directories cannot be read, Root returns the tree built from the readable entries together with a non-nil error joining every read failure (see errors.Join). A subdirectory that fails to read is omitted from its parent, but its readable siblings are kept.
The returned nodes have a nil Tree and filesystem-relative path IDs; New overwrites both with the owning Tree pointer and the canonical JSON path IDs. The node tree must therefore be passed to New (as the JsVar value) before rendering or any path operation, which otherwise dereference the nil Tree and panic.
func (*Node) GetSelected ¶
GetSelected returns the name-paths (root-to-node name lists) of all selected nodes.
Selection is reported and matched by name-path, not by the unique node identity used on the wire. If sibling nodes share the same name their name-paths are identical, so the round-trip is lossy: Node.SetSelected cannot tell them apart and will select every sibling sharing a selected name-path. Give siblings distinct names if they must be addressed independently through this API.
func (*Node) JawsPathSet ¶
JawsPathSet runs after a node's selected flag has been set on the server-side tree; it broadcasts a jawstreeSetPath JsCall so the change is reflected in the rendered tree of every client sharing this Tree.
func (*Node) JawsSetPath ¶ added in v0.500.0
JawsSetPath restricts browser-initiated mutations to the per-node "selected" flag.
Any other path, a non-bool value, or an out-of-range child index is rejected without mutating the tree, so a WebSocket client cannot change node names, ids, the children slice, or any other Node field by path. This is the server-side enforcement of the "server holds the truth" contract for Tree.
The path is resolved by navigating the Children slice ourselves with strict in-range index bounds rather than delegating to the generic JsVar path-setter (github.com/linkdata/jq.Set), which sets arbitrary json-tagged fields and grows a slice by one when asked to set index == len.
func (*Node) MarshalJSON ¶ added in v0.300.0
MarshalJSON writes the Quercus.js JSON shape for node (delegating to the canonical marshalJSON encoder).
func (*Node) SetSelected ¶
SetSelected applies the given selected name-paths and returns the nodes that changed.
Nodes are matched by name-path (see Node.GetSelected); when sibling nodes share a name they are selected or deselected together, since their name-paths are indistinguishable.
It mutates the shared Node tree; on a rendered Tree, hold the Tree's write lock while calling it (see the Node concurrency note).
type Option ¶ added in v0.300.0
type Option int
Option configures a Tree.
const ( // SearchEnabled enables tree search controls. SearchEnabled Option = (1 << iota) // InitiallyExpanded renders nodes expanded initially. InitiallyExpanded // MultiSelectEnabled allows multiple selected nodes. MultiSelectEnabled // ShowSelectAllButton shows a select-all control. ShowSelectAllButton // ShowInvertSelectionButton shows an invert-selection control. ShowInvertSelectionButton // ShowExpandCollapseAllButtons shows expand/collapse-all controls. ShowExpandCollapseAllButtons // NodeSelectionDisabled disables node selection. NodeSelectionDisabled // CascadeSelectChildren cascades selection to child nodes. CascadeSelectChildren // CheckboxSelectionEnabled renders checkbox selection controls. CheckboxSelectionEnabled )
type Tree ¶
Tree renders and updates a Quercus.js tree bound to a ui.JsVar.
func New ¶
New returns a tree widget with id, jsvar and options.
The id is used both as a JavaScript variable name and as a URL path segment for the init script, so it must be non-empty and contain only the characters [A-Za-z0-9_$]; otherwise New panics. Validating here turns what would otherwise be a confusing render-time "illegal jsvar name" error and a 400 on the init-script route into an immediate, clear failure.
New initializes node IDs and tree back-pointers in jsvar.Ptr. It panics if jsvar or jsvar.Ptr is nil, or if id is not a valid name.
func (*Tree) JawsRender ¶
JawsRender renders the hidden root data element and tree initialization script.
func (*Tree) JawsUpdate ¶ added in v0.300.0
JawsUpdate sends the latest tree JSON to the browser.