ToolGUI
This Go package provides a framework for rapidly building interactive data dashboards and web applications. It aims to offer a similar development experience to Streamlit for Python users.
⚠️ Under Development:
The API for this package is still under development, and may be subject to changes in the future.
Demo page: https://toolgui-demo.fly.dev/
Server-Client
Step by step Hello World
- Create
main.go
:
package main
import (
"github.com/VoileLab/toolgui/toolgui/tgcomp"
"github.com/VoileLab/toolgui/toolgui/tgexec"
"github.com/VoileLab/toolgui/toolgui/tgframe"
)
func main() {
app := tgframe.NewApp()
app.AddPage("index", "Index", func(p *tgframe.Params) error {
tgcomp.Text(p.Main, "Hello world")
return nil
})
tgexec.NewWebExecutor(app).StartService(":3001")
}
- Create go.mod and download toolgui:
go mod init toolgui-helloworld
go mod tidy
- Run helloworld
go run main.go
Explain
- Create a ToolGUI App: The
App
intance include the info that app needs.
app := tgframe.NewApp()
- Register a page in App: Tell
App
instance, we will have a page in the App.index
is the name.Index
is the title.
app.AddPage("index", "Index", ...)
- The Page Func: Draw a text component in the Main container.
func(p *tgframe.Params) error {
tgcomp.Text(p.Main, "Hello world")
return nil
}
- WebExecuter: The
App
only includes the logic of app, but not includes GUI. The we executer provide web server GUI interface forApp
.
tgexec.NewWebExecutor(app).StartService(":3001")
How it works?
Basic
The key concept is that in the Page Function, the UI component interact immediately with the running logic.
For example:
if tgcomp.Button(p.State, p.Main, "Click me") {
tgcomp.Text(p.Main, "Hi")
}
In the first call (For example: When the web page is entering.),
The Click me
button will render, but the Hi
will not render.
Since the button is not clicked in the first round.
When the user clicks the button, the Page Function will be called again.
In this round, tgcomp.Button
will return true
and Hi
will render.
How?
Since we delcare a button which its id is Click me
, and when the button clicked,
we store true
with key Click me
into p.State
.
When entering tccinput.Button
, it will check if there is any data store
in p.State
with key Click me
.
Server-Client Architecture
Server-Client need to handle a more complex part: multiple state for multiple users.
Hence we need a state_id
for each state.
The server part is responsible for the state pool.
App
The App includes the info of
- Navbar: The navbar is the topmost part of the app.
- Pages: The App hold a ordered map from page name to page's data.
Navbar
Here is the navbar of toolgui-demo.
The left part will be buttons which nav to the pages in App. The right part will be two button:
- Rerun: Rerun the Page Func without changing any state.
- Dark/Light Mode Switch: Switch the theme of the app.
The button of current page will be highlighted with a specific style.
Page
Parameters
We can config the page's:
-
Name: Or ID. The name of the page should be unique. In the web GUI provider, the name of a page will be used as the path of the page.
-
Title: In the web GUI provider, The title of a page will be used as the title of the page and text of button on the navbar.
-
Emoji: Optional. The emoji will be used as a icon on navbar and browser favicon.
The config type in package is:
type PageConfig struct {
Name string `json:"name"`
Title string `json:"title"`
Emoji string `json:"emoji"`
}
Page Function
The function signature of Page Function is defined as:
type RunFunc func(p *Params) error
Where Params
contains these parameters to operate the page:
type Params struct {
State *State
Main *Container
Sidebar *Container
}
Main
and Sidebar
is the root container component of the main and sidebar part
shown in the layout image.
We can show a text in the Main container by:
tgcomp.Text(p.Main, "Hello")
The State
in Params
provided for
- The component that need to pass state. For example: the checked state of checkbox.
- The state that user need to store. For example: The todo items in the Todo App.
Example for adding a page
- No emoji icon
app.AddPage("index", "Index", Main)
- With a emoji icon
app.AddPageByConfig(&tgframe.PageConfig{
Name: "page2",
Title: "Page2",
Emoji: "🔄",
}, Page2)
State Storage
-
Faster access: Frequently used data can be retrieved from the cache much faster than recalculating it or fetching it from an external source every time. This improves the application's overall performance.
-
Reduced resource usage: By avoiding redundant calculations and external data fetching, the app can conserve resources like CPU and network bandwidth.
App Cache
An app-level cache stores data for the entire duration of the application's execution, from launch to termination.
This cache is not provided by ToolGUI and needs to be implemented by the developer.
For example:
type App1 struct {
sync.Map data
}
func (app *App) QueryData(key string) {
if v, ok := data.Load(key); ok{
return v
}
// ...
v, _ := data.LoadOrStore(key, calVal)
return v
}
func (app *App) Page1(...) {
tgcomp.text(app.QueryData(...))
}
Additional Considerations:
-
Cache Invalidation: As the application runs, the underlying data sources might change. It's crucial to have a strategy to invalidate cached data when necessary to ensure consistency. This could involve periodically refreshing the cache or implementing mechanisms to detect changes in the source data.
-
Memory Usage: App-level caches can consume memory. It's essential to choose appropriate data structures and cache eviction policies to balance performance gains with memory constraints.
State Cache
State-level cache stores data specific to the current view or "page" displayed to the user. This data lost when the user navigates away from the page or refreshes it.
Example of variable
Here we provide a state-level TODO App example. It stores list of todos in the state:
type TODOList struct {
items []string
}
func (t *TODOList) add(item string) {
t.items = append(t.items, item)
}
func Main(p *tgframe.Params) error {
tgcomp.Title(p.Main, "Example for Todo App")
todoList := p.State.Default("todoList", &TODOList{}).(*TODOList)
inp := tgcomp.Textbox(p.State, p.Main, "Add todo")
if tgcomp.Button(p.State, p.Main, "Add") {
todoList.add(inp)
}
for i, todo := range todoList.items {
tgcomp.TextWithID(p.Main,
fmt.Sprintf("%d: %s", i, todo),
fmt.Sprintf("todo_%d", i))
}
return nil
}
Example of function
Here we provide a state-level cache for a function.
// getFiles unarchive cbz file and return list of file names,
// since unarchive is time-consuming, we use state-level cache to store the result
func getFiles(p *tgframe.Params, f *tcinput.FileObject) ([]string, error) {
key := fmt.Sprintf("%s_%s_%x", f.Name, f.Type, md5.Sum(f.Bytes))
v := p.State.GetFuncCache(key)
if v != nil {
slog.Debug("cache found")
return v.([]string), nil
}
buf := bytes.NewReader(f.Bytes)
cbzFp, err := zip.NewReader(buf, buf.Size())
if err != nil {
// don't store nil to cache
return nil, err
}
ret := []string{}
for _, f := range cbzFp.File {
ret = append(ret, f.Name)
}
// ok, we can store to cache
p.State.SetFuncCache(key, ret)
return ret, nil
}
func FuncCachePage(p *tgframe.Params) error {
cbzfile := tgcomp.Fileupload(p.State, p.Sidebar, "CBZ File", "application/x-cbz")
if cbzfile == nil {
return nil
}
files, err := getFiles(p, cbzfile)
if err != nil {
return err
}
for i, f := range files {
tgcomp.Text(p.Main, fmt.Sprintf("%d: %s", i, f))
}
return nil
}
Components
Component Tree / Forest
When every component created, we need to assign where it should generate. The root will be Main Container or Sidebar Container. Hence the relation between components is trees.
For example, if a page function implements as:
tgcomp.Text(p.Main, "Text")
tgcomp.Button(p.State, p.Main, "Button")
box := tgcomp.Box("box")
tgcomp.Text(box, "Text1")
tgcomp.Text(box, "Text2")
Then the Component Tree will be:
Content Components
The content components show basic content. It doesn't return any value to user.
import "github.com/VoileLab/toolgui/toolgui/tgcomp"
The demo page can be found here: https://toolgui-demo.fly.dev/content
Title
Title component display a title.
API
func Title(c *tgframe.Container, text string)
func TitleWithID(c *tgframe.Container, text string, id string)
c
is Parent container.text
is the title text.id
is a user specific element id.
Example
tgcomp.Title(p.Main, "Title")
Subtitle
Subtitle component display a subtitle.
API
func Subtitle(c *tgframe.Container, text string)
func SubtitleWithID(c *tgframe.Container, text string, id string)
c
is Parent container.text
is the subtitle text.id
is a user specific element id.
Example
tgcomp.Subtitle(p.Main, "Subtitle")
Text
Text component display a text.
API
func Text(c *tgframe.Container, text string)
func TextWithID(c *tgframe.Container, text string, id string)
c
is Parent container.text
is the text.id
is a user specific element id.
Example
tgcomp.Text(p.Main, "Text")
Image
Image component display an image.
API
Interface
func Image(c *tgframe.Container, img any)
func ImageWithConf(c *tgframe.Container, img any, conf *tgcomp.ImageConf)
Parameters
c
is Parent container.img
is the image.image.Image
: an image.Image[]byte
: a byte arraystring
: a url or base64 encoded image- example:
- url:
https://http.cat/100
- base64 uri:
data:image/png;base64,...
- url:
- example:
conf
is the configuration of the image.
// ImageConf is the configuration for the Image component
type ImageConf struct {
// Width is the width of the image (e.g. "100px", "50%")
Width string
// Format is the format of the image, default is "png"
Format ImageFormat
// ID is the unique identifier for this image component
ID string
}
Example
tgcomp.Image(p.Main, "https://http.cat/100")
Code
API
Interface
func Code(c *tgframe.Container, code string)
func CodeWithConf(c *tgframe.Container, code string, conf *CodeConf)
Parameters
c
: Parent container.code
: Code to display.conf
: Configuration for the code component.
// CodeConf provide extra config for Code Component.
type CodeConf struct {
// Language is language of code block, leave empty to use `go`
Language string
// ID is id of the component
ID string
}
Example
tgcomp.Code(c, "package main\n\nfunc main() {\n\tprintln(\"Hello, World!\")")
tgcomp.CodeWithConf(c, "package main\n\nfunc main() {\n\tprintln(\"Hello, World!\")",
&tgcomp.CodeConf{
Language: "go",
ID: "mycode",
})
Markdown
API
Interface
func Markdown(c *tgframe.Container, markdown string)
func MarkdownWithID(c *tgframe.Container, markdown string, id string)
Parameters
c
: Parent container.markdown
: Markdown content to display.id
: Unique component ID.
Example
tgcomp.Markdown(c, "* Hello, World!")
tgcomp.MarkdownWithID(c, "* Hello, World!", "my-markdown")
Divider
Divider component display a horizontal line.
API
func Divider(c *tgframe.Container)
func DividerWithID(c *tgframe.Container, id string)
c
is Parent container.id
is a user specific element id.
Example
tgcomp.Divider(p.Main)
Link
Link component display a link.
API
func Link(c *tgframe.Container, text, url string)
func LinkWithID(c *tgframe.Container, text, url, id string)
c
is Parent container.text
is the link text.url
is the link url.id
is a user specific element id.
Example
tgcomp.Link(p.Main, "Link", "https://www.example.com/")
Latex
Latex component is used to display LaTeX content.
API
func Latex(c *tgframe.Container, latex string)
func LatexWithID(c *tgframe.Container, latex string, id string)
c
is the container to add the LaTeX component to.latex
is the LaTeX content to display.
Example
tgcomp.Latex(c, "E = mc^2")
Data Components
The data components display data in some special form.
import "github.com/VoileLab/toolgui/toolgui/tgcomp"
The demo page can be found here: https://toolgui-demo.fly.dev/data
JSON
JSON component display the JSON representation of an object.
API
func JSON(c *tgframe.Container, v any)
c
is Parent container.v
is the object.- string: assume to be a serialized JSON string.
- other: assume to be a struct and will be converted to a JSON string.
Example
type DemoJSONHeader struct {
Type int
}
type DemoJSON struct {
Header DemoJSONHeader
IntValue int
URL string
IsOk bool
}
tgcomp.JSON(p.Main, &DemoJSON{})
Table
Table component display a table.
API
func Table(c *tgframe.Container, head []string, table [][]string)
c
is Parent container.head
is the head of table.body
is the body of table.
Example
tgcomp.Table(p.Main, []string{"a", "b"}, [][]string{{"1", "2"}, {"3", "4"}})
Input Components
The input components provide UI for app-user to input their data.
import "github.com/VoileLab/toolgui/toolgui/tgcomp"
The demo page can be found here: https://toolgui-demo.fly.dev/input
Textarea
Textarea create a textarea and return its value.
API
Interface
func Textarea(s *tgframe.State, c *tgframe.Container, label string) string
func TextareaWithConf(s *tgframe.State, c *tgframe.Container, label string, conf *tgcomp.TextareaConfig) string
Parameters
s
is State.c
is Parent container.label
is the label for textbox.height
is heigh of the textarea.
// TextareaConf is the configuration for a textarea.
type TextareaConf struct {
// Height is the height of the textarea. default value is 3.
Height int `json:"height"`
// Default is the default value of the textarea.
Default string `json:"default"`
// Color defines the color of the textarea
Color tcutil.Color
// ID is the unique identifier for this textarea component
ID string
}
Example
textareaValue := tgcomp.Textarea(p.State, p.Main, "Textarea", 5)
tgcomp.TextWithID(p.Main, "Value: "+textareaValue, "textarea_result")
Textbox
Textbox create a textbox and return its value.
API
Interface
func Textbox(s *tgframe.State, c *tgframe.Container, label string) string
func TextboxWithConf(s *tgframe.State, c *tgframe.Container, conf *tgcomp.TextboxConf) string
Parameters
s
is State.c
is Parent container.label
is the label for textbox.conf
is the configuration of the textbox.
// TextboxConf is the configuration for the Textbox component
type TextboxConf struct {
// Placeholder text to display in the textbox.
Placeholder string
// Maximum number of characters allowed in the textbox.
// If 0, there is no character limit.
MaxLength int
// Indicates whether the textbox should mask input as asterisks.
Password bool
// Indicates whether the textbox should be disabled.
Disabled bool
// Default value of the textbox.
Default string
// Color defines the color of the textbox
Color tcutil.Color
// ID is the unique identifier for this textbox component
ID string
}
Example
textboxValue := tgcomp.Textbox(p.State, p.Main, "Textbox")
tgcomp.TextWithID(p.Main, "Value: "+textboxValue, "textbox_result")
Fileupload
Fileupload create a fileupload and return its selected file.
API
type FileObject struct {
Name string `json:"name"`
Type string `json:"type"`
Size int `json:"size"`
Bytes []byte `json:"_"`
}
func Fileupload(s *tgframe.State, c *tgframe.Container, label, accept string) *FileObject
s
is State.c
is Parent container.label
is the label for options group.accept
is the file type to accept.- Return the selected file object. nil if no file is selected.
Example
fileObj := tgcomp.Fileupload(p.State, p.Main, "Fileupload", ".jpg,.png")
if fileObj != nil {
tgcomp.Text(p.Main, "Fileupload filename: "+fileObj.Name)
tgcomp.Text(p.Main, fmt.Sprintf("Fileupload bytes length: %d", len(fileObj.Bytes)))
}
Download Button
DownloadButton create a download button component.
API
Interface
func DownloadButton(s *tgframe.State, c *tgframe.Container, text string, body []byte) bool
func DownloadButtonWithConf(s *tgframe.State, c *tgframe.Container, text string, body []byte, conf *DownloadButtonConf) bool
Parameters
c
is Parent container.text
is the link text.body
is the bytes of file.filename
is the file name.conf
is the configuration of the download button.
type DownloadButtonConf struct {
// MIME specifies the Multipurpose Internet Mail Extension (MIME) type of the downloaded content.
// Defaults to "application/octet-stream" if not provided.
MIME string
// Color defines the color of the download button.
Color tcutil.Color
// Disabled indicates whether the download button should be initially disabled.
Disabled bool
// Filename sets the suggested filename for the downloaded content when clicked.
Filename string
ID string
}
Example
tgcomp.DownloadButton(p.State, p.Main,
"Download", []byte("123"), "123.txt")
Checkbox
Checkbox create a checkbox and return true if it's checked.
API
Interface
func Checkbox(s *tgframe.State, c *tgframe.Container, label string) bool
func CheckboxWithConf(s *tgframe.State, c *tgframe.Container, label string, conf *CheckboxConf) bool
Parameters
s
is State.c
is Parent container.label
is the text on checkbox.
// CheckboxConf is the configuration for a checkbox.
type CheckboxConf struct {
// Default is true if the checkbox is default checked.
Default bool
// Disabled is true if the checkbox is disabled.
Disabled bool
// ID is the ID of the checkbox.
ID string
}
Example
checkboxValue := tgcomp.Checkbox(p.State, p.Main, "Checkbox")
if checkboxValue {
tgcomp.TextWithID(p.Main, "Value: true", "checkbox_result")
} else {
tgcomp.TextWithID(p.Main, "Value: false", "checkbox_result")
}
Button
Button create a button and return true if it's clicked.
API
func Button(s *tgframe.State, c *tgframe.Container, label string) bool
func ButtonWithConf(s *tgframe.State, c *tgframe.Container, conf *tgcomp.ButtonConf) bool
s
is State.c
is Parent container.label
is the text on button.conf
is the configuration of the button.
// ButtonConf is the configuration for the Button component
type ButtonConf struct {
// Color defines the color of the button
Color tcutil.Color
// Disabled indicates whether the button should be initially disabled
Disabled bool
// ID is the unique identifier for this button component
ID string
}
Example
btnClicked := tgcomp.Button(p.State, p.Main, "button")
if btnClicked {
tgcomp.TextWithID(p.Main, "Value: true", "button_result")
} else {
tgcomp.TextWithID(p.Main, "Value: false", "button_result")
}
Select
Select create a select dropdown list and return its selected value.
API
func Select(s *tgframe.State, c *tgframe.Container, label string, items []string) *int
s
is State.c
is Parent container.label
is the label for select.items
is the list of options.- Return the index of the selected item, 0-indexed. nil if no item is selected.
Example
selIndex := tgcomp.Select(p.State, p.Main, "Select", []string{"Value1", "Value2"})
if selIndex != nil {
tgcomp.TextWithID(p.Main, fmt.Sprintf("Value: Value%d", *selIndex + 1), "select_result")
}
Radio
Radio create a group of radio items and return its selected value.
API
func Radio(s *tgframe.State, c *tgframe.Container, label string, items []string) *int
s
is State.c
is Parent container.label
is the label for options group.items
is the list of options.- Return the index of the selected item, 0-indexed. nil if no item is selected.
Example
radioIndex := tgcomp.Radio(p.State, p.Main, "Radio", []string{"Value3", "Value4"})
if radioIndex != nil {
tgcomp.TextWithID(p.Main, fmt.Sprintf("Value: Value%d", *radioIndex + 3), "radio_result")
}
Datepicker
Datepicker create a datepicker and return its selected date.
API
type Date struct {
Year int
Month int
Day int
}
func Datepicker(s *tgframe.State, c *tgframe.Container, label string) *Date
s
is State.c
is Parent container.label
is the label for datepicker.- Return the selected date. nil if no date is selected.
Example
dateValue := tgcomp.Datepicker(p.State, p.Main, "Datepicker")
if dateValue != nil {
text := fmt.Sprintf("Value: %04d-%02d-%02d", dateValue.Year, dateValue.Month, dateValue.Day)
tgcomp.TextWithID(p.Main, text, "datepicker_result")
}
Timepicker
Timepicker create a timepicker and return its selected time.
API
type Time struct {
Hour int
Minute int
}
func Timepicker(s *tgframe.State, c *tgframe.Container, label string) *Time
s
is State.c
is Parent container.label
is the label for timepicker.- Return the selected time. nil if no time is selected.
Example
timeValue := tgcomp.Timepicker(p.State, p.Main, "Timepicker")
if timeValue != nil {
text := fmt.Sprintf("Value: %02d:%02d", timeValue.Hour, timeValue.Minute)
tgcomp.TextWithID(p.Main, text, "timepicker_result")
}
Datetimepicker
Datetimepicker create a datetimepicker and return its selected datetime.
API
func Datetimepicker(s *tgframe.State, c *tgframe.Container, label string) *time.Time
s
is State.c
is Parent container.label
is the label for datetimepicker.- Return the selected datetime. nil if no datetime is selected.
Example
dateValue := tgcomp.Datetimepicker(p.State, p.Main, "Datetimepicker")
if dateValue != nil {
tgcomp.TextWithID(p.Main, "Value: "+dateValue.Format("2006-01-02 15:04"), "datetimepicker_result")
}
Number Input
NumberInput create a number input and return its value.
API
Interface
func NumberFloat64(s *tgframe.State, c *tgframe.Container, label string) *float64
func NumberWithConfFloat64(s *tgframe.State, c *tgframe.Container, label string, conf *NumberConf[float64]) *float64
func NumberInt64(s *tgframe.State, c *tgframe.Container, label string) *int64
func NumberWithConfInt64(s *tgframe.State, c *tgframe.Container, label string, conf *NumberConf[int64]) *int64
Parameters
s
is State.c
is Parent container.label
is the label of the number input.
// NumberConf is the configuration for a number component.
type NumberConf[T float64 | int64] struct {
// Default is the default value of the number component.
Default *T
// Min is the minimum value of the number component.
Min *T
// Max is the maximum value of the number component.
Max *T
// Step is the step of the number component.
Step *T
// Color is the color of the number component.
Color tcutil.Color
// Placeholder is the placeholder of the number component.
Placeholder string
// Disabled is the disabled state of the number component.
Disabled bool
// ID is the ID of the number component.
ID string
}
func (c *NumberConf[T]) SetDefault(default T) *NumberConf[T] {
c.Default = &default
return c
}
func (c *NumberConf[T]) SetMin(min T) *NumberConf[T] {
c.Min = &min
return c
}
func (c *NumberConf[T]) SetMax(max T) *NumberConf[T] {
c.Max = &max
return c
}
func (c *NumberConf[T]) SetStep(step T) *NumberConf[T] {
c.Step = &step
return c
}
Example
numberValue := tgcomp.NumberWithConfFloat64(numberCompCol, p.State, "Number",
(&tgcomp.NumberConf[float64]{
Placeholder: "input the value here",
Color: tcutil.ColorSuccess,
}).SetMin(10).SetMax(20).SetStep(2))
valStr := ""
if numberValue != nil {
valStr = fmt.Sprint(*numberValue)
}
tgcomp.TextWithID(numberCompCol, "Value: "+valStr, "number_result")
Form
Form create a form component. The input components in this component will not auto trigger script execution. The user need to click the submit button to trigger the script execution.
API
Interface
func Form(c *tgframe.Container, id string) *tgframe.Container
Parameters
c
is Parent container.id
is the ID of the form.
Example
tgcomp.Form(formCompCol, "form").With(func(c *tgframe.Container) {
a = tgcomp.Number(c, p.State, "a")
b = tgcomp.Number(c, p.State, "b")
})
if a != nil && b != nil {
tgcomp.Text(formCompCol, fmt.Sprintf("int(a) + int(b) = %d", int(*a)+int(*b)))
}
Layout Components
The layout components display control component layout and position.
import "github.com/VoileLab/toolgui/toolgui/tgcomp"
The demo page can be found here: https://toolgui-demo.fly.dev/layout
Container
Container is the most basic layout component. Their definition are in tgframe. The Main "container" and Sidebar "container" are Containers.
Usage
To create a container under a container. Just call the function:
func (c *Container) AddContainer(id string) *Container
- ID should be unique.
Box
Box provide a simple container that show box style.
Usage
Box create a box container.
func Box(c *tgframe.Container, id string) *tgframe.Container
c
: Parent container.id
: Unique component ID.
Example
box := tgcomp.Box(boxCompCol, "box")
tgcomp.Text(box, "A box!")
Column
Column provides columns layout.
Usage
- Column create N columns.
- Column2 create 2 columns.
- Column3 create 3 columns.
- EqColumn create N columns with equal width.
- EqColumn1 create 1 column with equal width.
- EqColumn2 create 2 columns with equal width.
- EqColumn3 create 3 columns with equal width.
- EqColumn4 create 4 columns with equal width.
- EqColumn5 create 5 columns with equal width.
func Column(c *tgframe.Container, id string, n uint) []*tgframe.Container
func Column2(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container)
func Column3(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)
func EqColumn(c *tgframe.Container, id string, n uint) []*tgframe.Container
func EqColumn1(c *tgframe.Container, id string) *tgframe.Container
func EqColumn2(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container)
func EqColumn3(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)
func EqColumn4(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
func EqColumn5(c *tgframe.Container, id string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
c
: Parent container.id
: Unique component ID.n
: Number of column.
Example
cols := tgcomp.Column(colCompCol, "cols", 3)
for i, col := range cols {
tgcomp.Text(col, fmt.Sprintf("col-%d", i))
}
Expand
Expand provides expandable layout.
API
func Expand(c *tgframe.Container, title string, expanded bool) *tgframe.Container
c
is the container to add the expandable component to.title
is the title of the expandable component.expanded
is the initial expanded state of the expandable component.
Example
tgcomp.Expand(c, "Expand", true)
Tab
Tab component is used to create a tabbed interface.
API
func Tab(c *tgframe.Container, tabs []string) []*tgframe.Container
func Tab2(c *tgframe.Container, tab1, tab2 string) (*tgframe.Container, *tgframe.Container)
func Tab3(c *tgframe.Container, tab1, tab2, tab3 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container)
func Tab4(c *tgframe.Container, tab1, tab2, tab3, tab4 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
func Tab5(c *tgframe.Container, tab1, tab2, tab3, tab4, tab5 string) (*tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container, *tgframe.Container)
c
is the container to add the tab component to.tabs
is the list of tab titles.tab1
,tab2
,tab3
,tab4
,tab5
are the tab titles.
Example
tabs := tgcomp.Tab(c, []string{"Tab 1", "Tab 2", "Tab 3"})
for _, tab := range tabs {
tgcomp.Text(tab, "Hello World")
}
Misc Components
The misc components provide various UI components that are not classified under other categories.
import "github.com/VoileLab/toolgui/toolgui/tgcomp"
The demo page can be found here: https://toolgui-demo.fly.dev/misc
Echo
Echo is a component that execute the code of lambda function and display the code of lambda function.
API
func Echo(c *tgframe.Container, code string, lambda func())
Parameters
c
: Parent container.code
: Code of current file. The user should use go embed syntax to embed the code.lambda
: Lambda function to execute.
Example
//go:embed main.go
var code string
tgcomp.Echo(c, code, func() {
println("Hello, World!")
})
Message
Message is a component that displays a message.
API
Interface
func Message(c *tgframe.Container, text string)
func MessageInfo(c *tgframe.Container, text string)
func MessageSuccess(c *tgframe.Container, text string)
func MessageWarning(c *tgframe.Container, text string)
func MessageDanger(c *tgframe.Container, text string)
func MessageWithConf(c *tgframe.Container, text string, conf *MessageConf)
Parameters
-
c
: Parent container. -
text
: Text to display. -
conf
: Configuration for the message component. -
Message[Info|Success|Warning|Danger]
: Create a message with a specific color.
type MessageConf struct {
// Title is the title of the message. Optional.
Title string
// Color is the color of the message. Default is tcutil.ColorNull.
Color tcutil.Color
// ID is the unique identifier of the component.
ID string
}
Example
tgcomp.Message(c, "Hello, World!")
tgcomp.MessageInfo(c, "Hello, World!")
tgcomp.MessageWithConf(c, "Hello, World!", &tgcomp.MessageConf{
Title: "Info",
Color: tcutil.ColorInfo,
})
Progress Bar
ProgressBar is a component that displays a progress bar.
API
Interface
func ProgressBar(c *tgframe.Container, value int, label string) *ProgressBarComponent
Parameters
c
: Parent container.value
: Value of the progress bar.label
: Label of the progress bar.
Example
bar := tgcomp.ProgressBar(c, 50, "Progress")
for i := 0; i <= 100; i++ {
bar.SetValue(i)
time.Sleep(100 * time.Millisecond)
}
bar.SetLabel("Completed")
Iframe (Experimental)
Iframe is used to show a html in a iframe.
Warning: This component is experimental 🧪 and may not work as expected.
API
func Iframe(c *tgframe.Container, html string, script bool)
func IframeWithID(c *tgframe.Container, html string, script bool, id string)
c
is the container to add the iframe to.html
is the html to show in the iframe.script
is used to allow the iframe to run javascript. (notice that this is not secure)id
is the user specific id.
Examples
Simple
Show h1 element in the iframe.
tgcomp.Iframe(p.Main, "<h1>Hello World</h1>", true)
Script
Show a button that updates the iframe content.
htmlWithScript := `
<b id="test">Hello world not changed</b>
<script>
const element = document.getElementById('test');
element.innerText = 'Hello world gen by script';
</script>`
tgcomp.IframeWithID(
p.Main,
htmlWithScript,
true,
"iframe_with_script")
Interactive
Show a button that updates the iframe content.
In the iframe, there are four functions injected:
window.update(data)
- update the iframe content with the data.window.theme
- current themewindow.upload(data)
- upload a file to the serverwindow.props
- props of the iframe component
tgcomp.IframeWithID(
p.Main,
`<button id="btn">Click me to update</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click', (event) => {
window.update({type: "click", id: "iframe_with_interactive_btn"});
});
</script>`,
true,
"iframe_with_interactive")
clickStatus := p.State.GetClickID() == "iframe_with_interactive_btn"
tgcomp.Text(p.Main, fmt.Sprintf("Status: %v", clickStatus))
Html
Html component is used to display html content.
API
func Html(c *tgframe.Container, html string)
func HtmlWithID(c *tgframe.Container, html string, id string)
Parameters
c
: Parent container.html
: Html content to display.id
: Id of the component.
Example
tgcomp.Html(p.Main, "<b>Hello world gen by html component</b>")