more docs
This commit is contained in:
parent
b2c6b52075
commit
df90b00ee2
11 changed files with 45 additions and 189 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> This is a prerelease version and generally should not be used at this time. Watch on github for the stable release!
|
> htmgo is in alpha release. Please report any issues on GitHub.
|
||||||
|
|
||||||
## **htmgo**
|
## **htmgo**
|
||||||
|
|
||||||
|
|
@ -37,7 +37,5 @@ func IndexPage(ctx *h.RequestContext) *h.Page {
|
||||||
6. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks
|
6. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks
|
||||||
|
|
||||||
**get started:**
|
**get started:**
|
||||||
> [!WARNING]
|
|
||||||
> This is a prerelease version and generally should not be used at this time. Watch on github for the stable release!
|
|
||||||
|
|
||||||
View documentation on [htmgo.dev](https://htmgo.dev/).
|
View documentation on [htmgo.dev](https://htmgo.dev/).
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,12 @@ func CompleteAllIcon(list []*ent.Task) *h.Element {
|
||||||
return item.CompletedAt == nil
|
return item.CompletedAt == nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return h.Div(
|
return h.Button(
|
||||||
h.ClassX("absolute top-0 left-0 p-4 rotate-90 text-2xl cursor-pointer", map[string]bool{
|
h.TextF("Complete %s tasks", notCompletedCount),
|
||||||
"text-slate-400": notCompletedCount > 0,
|
h.PostPartialWithQs(CompleteAll,
|
||||||
}), h.Text("❯"),
|
h.NewQs("complete",
|
||||||
h.PostPartialWithQs(CompleteAll, h.NewQs("complete", h.Ternary(notCompletedCount > 0, "true", "false"))),
|
h.Ternary(notCompletedCount > 0, "true", "false"),
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,12 +269,10 @@ func ToggleCompleted(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
||||||
func CompleteAll(ctx *h.RequestContext) *h.Partial {
|
func CompleteAll(ctx *h.RequestContext) *h.Partial {
|
||||||
service := tasks.NewService(ctx.ServiceLocator())
|
service := tasks.NewService(ctx.ServiceLocator())
|
||||||
|
|
||||||
service.SetAllCompleted(ctx.QueryParam("complete") == "true")
|
service.SetAllCompleted(ctx.QueryParam("complete") == "true")
|
||||||
|
return h.SwapPartial(ctx,
|
||||||
list, _ := service.List()
|
Card(ctx),
|
||||||
|
)
|
||||||
return h.NewPartial(h.OobSwap(ctx, CardBody(list, getActiveTab(ctx))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearCompleted(ctx *h.RequestContext) *h.Partial {
|
func ClearCompleted(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
|
||||||
|
|
@ -27,32 +27,13 @@ func Button(props ButtonProps) h.Ren {
|
||||||
|
|
||||||
text := h.Text(props.Text)
|
text := h.Text(props.Text)
|
||||||
|
|
||||||
lifecycle := h.NewLifeCycle().
|
|
||||||
HxBeforeRequest(
|
|
||||||
h.AddAttribute("disabled", "true"),
|
|
||||||
h.SetText("Loading..."),
|
|
||||||
h.AddClass("bg-gray-400"),
|
|
||||||
).
|
|
||||||
HxAfterRequest(
|
|
||||||
h.RemoveAttribute("disabled"),
|
|
||||||
h.RemoveClass("bg-gray-400"),
|
|
||||||
h.SetText(props.Text),
|
|
||||||
).
|
|
||||||
HxOnMutationError(
|
|
||||||
h.SetText("failed"),
|
|
||||||
h.AddClass("bg-red-400"),
|
|
||||||
h.RemoveAttribute("disabled"),
|
|
||||||
)
|
|
||||||
|
|
||||||
button := h.Button(
|
button := h.Button(
|
||||||
h.If(props.Id != "", h.Id(props.Id)),
|
h.If(props.Id != "", h.Id(props.Id)),
|
||||||
h.If(props.Children != nil, h.Children(props.Children...)),
|
h.If(props.Children != nil, h.Children(props.Children...)),
|
||||||
h.If(props.Trigger != "", h.HxTrigger(props.Trigger)),
|
|
||||||
h.Class("flex gap-1 items-center border p-4 rounded cursor-hover", props.Class),
|
h.Class("flex gap-1 items-center border p-4 rounded cursor-hover", props.Class),
|
||||||
h.If(props.Get != "", h.Get(props.Get)),
|
h.If(props.Get != "", h.Get(props.Get)),
|
||||||
h.If(props.Target != "", h.HxTarget(props.Target)),
|
h.If(props.Target != "", h.HxTarget(props.Target)),
|
||||||
h.IfElse(props.Type != "", h.Type(props.Type), h.Type("button")),
|
h.IfElse(props.Type != "", h.Type(props.Type), h.Type("button")),
|
||||||
lifecycle,
|
|
||||||
text,
|
text,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,11 @@ func SwapManyPartialWithHeaders(ctx *RequestContext, headers *Headers, swaps ...
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SwapPartial(ctx *RequestContext, swap *Element) *Partial {
|
||||||
|
return NewPartial(
|
||||||
|
SwapMany(ctx, swap))
|
||||||
|
}
|
||||||
|
|
||||||
func SwapManyPartial(ctx *RequestContext, swaps ...*Element) *Partial {
|
func SwapManyPartial(ctx *RequestContext, swaps ...*Element) *Partial {
|
||||||
return NewPartial(
|
return NewPartial(
|
||||||
SwapMany(ctx, swaps...),
|
SwapMany(ctx, swaps...),
|
||||||
|
|
@ -63,7 +68,7 @@ func SwapManyXPartial(ctx *RequestContext, swaps ...SwapArg) *Partial {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPartialPath(partial func(ctx *RequestContext) *Partial) string {
|
func GetPartialPath(partial PartialFunc) string {
|
||||||
return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name()
|
return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,3 +49,10 @@ func ClassIf(condition bool, value string) Ren {
|
||||||
}
|
}
|
||||||
return Empty()
|
return Empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AttributeIf(condition bool, name string, value string) Ren {
|
||||||
|
if condition {
|
||||||
|
return Attribute(name, value)
|
||||||
|
}
|
||||||
|
return Empty()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,18 @@ func IterMap[T any](m map[string]T, mapper func(key string, value T) *Element) *
|
||||||
}
|
}
|
||||||
|
|
||||||
func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
||||||
|
|
||||||
|
values := map[string]string{
|
||||||
|
"key": "value",
|
||||||
|
}
|
||||||
|
|
||||||
|
IterMap(values, func(key string, value string) *Element {
|
||||||
|
return Div(
|
||||||
|
Text(key),
|
||||||
|
Text(value),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
node := &Element{
|
node := &Element{
|
||||||
tag: "",
|
tag: "",
|
||||||
children: make([]Ren, len(items)),
|
children: make([]Ren, len(items)),
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func DocsPage(ctx *h.RequestContext) *h.Page {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**[This site](https://github.com/maddalax/htmgo/tree/master/htmgo-site) was written with htmgo!**
|
**[The site you are reading now](https://github.com/maddalax/htmgo/tree/master/htmgo-site) was written with htmgo!**
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
|
|
||||||
##### **Prerequisites:**
|
##### **Prerequisites:**
|
||||||
Go: https://go.dev/doc/install
|
1. Go: https://go.dev/doc/install
|
||||||
|
2. Familiarity with https://htmx.org and html/hypermedia
|
||||||
|
1. If you have not read the htmx docs, please do so before continuting, a lot of concepts below will be much more clear after.
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
##### 1. **Install htmgo**
|
##### 1. **Install htmgo**
|
||||||
|
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
## Pages ##
|
|
||||||
|
|
||||||
Pages are the entry point of an htmgo application.
|
|
||||||
|
|
||||||
A simple page may look like:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// route will be automatically registered based on the file name
|
|
||||||
func HelloHtmgoPage(ctx *h.RequestContext) *h.Page {
|
|
||||||
return h.NewPage(
|
|
||||||
h.Html(
|
|
||||||
h.HxExtension(h.BaseExtensions()),
|
|
||||||
h.Head(
|
|
||||||
h.Link("/public/main.css", "stylesheet"),
|
|
||||||
h.Script("/public/htmgo.js"),
|
|
||||||
),
|
|
||||||
h.Body(
|
|
||||||
h.Pf("Hello, htmgo!"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
htmgo uses [Echo Go](https://echo.labstack.com/docs/context) as its web server, ***h.RequestContext** is a thin wrapper around **echo.Context**. A page
|
|
||||||
must return *h.Page, and accept *h.RequestContext as a parameter
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
**Auto Registration**
|
|
||||||
|
|
||||||
htmgo uses file based routing. This means that we will automatically generate and register your routes with echo based on the files you have in the 'pages' directory.
|
|
||||||
|
|
||||||
For example, if you have a directory structure such as:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pages
|
|
||||||
index.go
|
|
||||||
users.go
|
|
||||||
users.$id //id parameter can be accessed in your page with ctx.Param("id")
|
|
||||||
```
|
|
||||||
|
|
||||||
it will get registered into Echo as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
/
|
|
||||||
/users
|
|
||||||
/users/:id
|
|
||||||
```
|
|
||||||
|
|
||||||
You may put any functions you like in your pages file, auto registration will **ONLY** register functions that return ***h.Page**
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
**Tips:**
|
|
||||||
|
|
||||||
Generally it is it recommended to abstract common parts of your page into its own component and re-use it, such as script tags, including styling, etc.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func RootPage(children ...h.Ren) *h.Element {
|
|
||||||
return h.Html(
|
|
||||||
h.HxExtension(h.BaseExtensions()),
|
|
||||||
h.Head(
|
|
||||||
h.Meta("viewport", "width=device-width, initial-scale=1"),
|
|
||||||
h.Link("/public/main.css", "stylesheet"),
|
|
||||||
h.Script("/public/htmgo.js"),
|
|
||||||
h.Style(`
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
`),
|
|
||||||
),
|
|
||||||
h.Body(
|
|
||||||
h.Class("bg-stone-50 min-h-screen overflow-x-hidden"),
|
|
||||||
partials.NavBar(false),
|
|
||||||
h.Fragment(children...),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
func UserPage(ctx *h.RequestContext) *h.Page {
|
|
||||||
return h.NewPage(
|
|
||||||
base.RootPage(
|
|
||||||
h.Div(
|
|
||||||
h.Pf("User ID: %s", ctx.Param("id")),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
## Partials ##
|
|
||||||
|
|
||||||
Partials are where things get interesting. Partials allow you to start adding interactivity to your website by swapping in content, setting headers, redirecting, etc.
|
|
||||||
|
|
||||||
Partials have a similiar structure to pages. A simple partial may look like:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func CurrentTimePartial(ctx *h.RequestContext) *h.Partial {
|
|
||||||
now := time.Now()
|
|
||||||
return h.NewPartial(
|
|
||||||
h.Div(
|
|
||||||
h.Pf("The current time is %s", now.Format(time.RFC3339)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will get automatically registered in the same way that pages are registered, based on the file path. This allows you to reference partials directly via the function itself when rendering them, instead of worrying about the route.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
I want to build a page that renders the current time, updating every second. Here is how that may look:
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
**pages/time.go**
|
|
||||||
|
|
||||||
```go
|
|
||||||
package pages
|
|
||||||
|
|
||||||
func CurrentTimePage(ctx *h.RequestContext) *h.Page {
|
|
||||||
return h.NewPage(
|
|
||||||
base.RootPage(
|
|
||||||
h.GetPartial(
|
|
||||||
partials.CurrentTimePartial,
|
|
||||||
"load, every 1s"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**partials/time.go**
|
|
||||||
|
|
||||||
```go
|
|
||||||
package partials
|
|
||||||
|
|
||||||
func CurrentTimePartial(ctx *h.RequestContext) *h.Partial {
|
|
||||||
now := time.Now()
|
|
||||||
return h.NewPartial(
|
|
||||||
h.Div(
|
|
||||||
h.Pf("The current time is %s", now.Format(time.RFC3339)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When the page load, the partial will be loaded in via htmx, and then swapped in every 1 second. With this
|
|
||||||
little amount of code and zero written javascript, you have a page that shows the current time and updates
|
|
||||||
every second.
|
|
||||||
|
|
||||||
|
|
@ -39,8 +39,10 @@ func Star() *h.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NavBar(expanded bool) *h.Element {
|
func NavBar(expanded bool) *h.Element {
|
||||||
prelease := h.Div(h.Class("bg-yellow-200 text-yellow-800 text-center p-2"),
|
prelease := h.A(h.Class("bg-yellow-200 text-yellow-800 text-center p-2 flex items-center justify-center"),
|
||||||
h.Text("This is a prerelease version and generally should not be used at this time. Watch on github for the stable release!"),
|
h.Href("https://github.com/maddalax/htmgo/issues"),
|
||||||
|
h.Attribute("target", "_blank"),
|
||||||
|
h.Text("htmgo is in alpha release. Please report any issues on GitHub."),
|
||||||
)
|
)
|
||||||
|
|
||||||
desktopNav := h.Nav(
|
desktopNav := h.Nav(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue