diff --git a/framework/h/header.go b/framework/h/header.go index 4c9c42b..afdb5a3 100644 --- a/framework/h/header.go +++ b/framework/h/header.go @@ -15,6 +15,14 @@ func PushUrlHeader(url string) *Headers { return NewHeaders(hx.PushUrlHeader, url) } +func PushQsHeader(ctx *RequestContext, qs *Qs) *Headers { + parsed, err := url.Parse(ctx.currentBrowserUrl) + if err != nil { + return NewHeaders() + } + return NewHeaders(hx.ReplaceUrlHeader, SetQueryParams(parsed.Path, qs)) +} + func CombineHeaders(headers ...*Headers) *Headers { m := make(Headers) for _, h := range headers { diff --git a/framework/h/qs.go b/framework/h/qs.go index 548157f..af67600 100644 --- a/framework/h/qs.go +++ b/framework/h/qs.go @@ -1,7 +1,6 @@ package h import ( - "github.com/maddalax/htmgo/framework/hx" "net/url" "strings" ) @@ -49,14 +48,6 @@ func (q *Qs) ToString() string { return builder.String() } -func PushQsHeader(ctx *RequestContext, qs *Qs) *Headers { - parsed, err := url.Parse(ctx.currentBrowserUrl) - if err != nil { - return NewHeaders() - } - return NewHeaders(hx.ReplaceUrlHeader, SetQueryParams(parsed.Path, qs)) -} - func GetQueryParam(ctx *RequestContext, key string) string { value := ctx.QueryParam(key) if value == "" { diff --git a/sandbox/assets/css/input.css b/sandbox/assets/css/input.css deleted file mode 100644 index bd6213e..0000000 --- a/sandbox/assets/css/input.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/sandbox/assets/css/tailwind.config.js b/sandbox/assets/css/tailwind.config.js deleted file mode 100644 index 07d3a7b..0000000 --- a/sandbox/assets/css/tailwind.config.js +++ /dev/null @@ -1,36 +0,0 @@ -const {join} = require("node:path"); -/** @type {import('tailwindcss').Config} */ -const root = join(__dirname, "../../"); -const contentGo = join(root, "**/*.go"); -const contentJs = join(root, "**/pages/**/*.js"); - - -module.exports = { - content: [contentGo, contentJs], - theme: { - extend: { - colors: { - background: 'hsl(224, 71.4%, 4.1%)', - foreground: 'hsl(0, 0%, 89%)', - card: 'hsl(224, 71.4%, 4.1%)', - cardForeground: 'hsl(0, 0%, 89%)', - popover: 'hsl(224, 71.4%, 4.1%)', - popoverForeground: 'hsl(0, 0%, 89%)', - primary: 'hsl(0, 0%, 89%)', - primaryForeground: 'hsl(220.9, 39.3%, 11%)', - secondary: 'hsl(215, 27.9%, 16.9%)', - secondaryForeground: 'hsl(0, 0%, 89%)', - muted: 'hsl(215, 27.9%, 16.9%)', - mutedForeground: 'hsl(217.9, 10.6%, 64.9%)', - accent: 'hsl(215, 27.9%, 16.9%)', - accentForeground: 'hsl(0, 0%, 89%)', - destructive: 'hsl(0, 62.8%, 30.6%)', - destructiveForeground: 'hsl(0, 0%, 89%)', - border: 'hsl(215, 27.9%, 16.9%)', - input: 'hsl(215, 27.9%, 16.9%)', - ring: 'hsl(216, 12.2%, 83.9%)', - }, - }, - }, - plugins: [], -}; diff --git a/sandbox/assets/css/tailwindcss b/sandbox/assets/css/tailwindcss deleted file mode 100755 index f012497..0000000 Binary files a/sandbox/assets/css/tailwindcss and /dev/null differ diff --git a/sandbox/database/database.go b/sandbox/database/database.go deleted file mode 100644 index 0378399..0000000 --- a/sandbox/database/database.go +++ /dev/null @@ -1,136 +0,0 @@ -package database - -import ( - "context" - "encoding/json" - "github.com/redis/go-redis/v9" - "sync" - "time" -) - -var ( - once sync.Once - rdb *redis.Client -) - -func Connect() *redis.Client { - once.Do(func() { - var ctx = context.Background() - var err error - rdb = redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - if err != nil { - panic(err) - } - - cmd := rdb.Ping(ctx) - - if cmd.Err() != nil { - panic(err) - } - }) - return rdb -} - -func Incr(key string) int64 { - db := Connect() - result := db.Incr(context.Background(), key) - return result.Val() -} - -func Set[T any](key string, value T) error { - db := Connect() - serialized, err := json.Marshal(value) - if err != nil { - return err - } - result := db.Set(context.Background(), key, serialized, time.Duration(0)) - return result.Err() -} - -func HSet[T any](set string, key string, value T) error { - db := Connect() - serialized, err := json.Marshal(value) - if err != nil { - return err - } - result := db.HSet(context.Background(), set, key, serialized) - return result.Err() -} - -func HIncr(set string, key string) int64 { - db := Connect() - result := db.HIncrBy(context.Background(), set, key, 1) - return result.Val() -} - -func HGet[T any](set string, key string) *T { - db := Connect() - val, err := db.HGet(context.Background(), set, key).Result() - if err != nil || val == "" { - return nil - } - result := new(T) - err = json.Unmarshal([]byte(val), result) - if err != nil { - return nil - } - return result -} - -func GetOrSet[T any](key string, cb func() T) (*T, error) { - db := Connect() - val, err := db.Get(context.Background(), key).Result() - if err == nil { - result := new(T) - err = json.Unmarshal([]byte(val), result) - if err != nil { - return nil, err - } - return result, nil - } - value := cb() - err = Set(key, value) - if err != nil { - return nil, err - } - return &value, nil -} - -func Get[T any](key string) (*T, error) { - db := Connect() - val, err := db.Get(context.Background(), key).Result() - if err != nil { - return nil, err - } - result := new(T) - err = json.Unmarshal([]byte(val), result) - if err != nil { - return nil, err - } - return result, nil -} - -func HList[T any](key string) ([]*T, error) { - db := Connect() - val, err := db.HGetAll(context.Background(), key).Result() - if err != nil { - return nil, err - } - result := make([]*T, len(val)) - - count := 0 - for _, t := range val { - item := new(T) - err = json.Unmarshal([]byte(t), item) - if err != nil { - return nil, err - } - result[count] = item - count++ - } - return result, nil -} diff --git a/sandbox/features/patient/patient-service.go b/sandbox/features/patient/patient-service.go deleted file mode 100644 index e7ee1db..0000000 --- a/sandbox/features/patient/patient-service.go +++ /dev/null @@ -1,49 +0,0 @@ -package patient - -import ( - "errors" - "github.com/google/uuid" - "github.com/labstack/echo/v4" - "starter-template/database" - "time" -) - -type Patient struct { - Name string - ReasonForVisit string - AppointmentDate time.Time - LocationName string -} - -type Service struct { - ctx echo.Context -} - -func NewService(ctx echo.Context) *Service { - return &Service{} -} - -type CreatePatientRequest struct { - Name string - ReasonForVisit string - LocationName string -} - -func (c *Service) Create(request CreatePatientRequest) error { - time.Sleep(time.Second) - database.HSet("patients", uuid.New().String(), Patient{ - Name: request.Name, - ReasonForVisit: request.ReasonForVisit, - AppointmentDate: time.Now(), - LocationName: "New York", - }) - return errors.New("error creating patient") -} - -func (c *Service) List() ([]*Patient, error) { - patients, err := database.HList[Patient]("patients") - if err != nil { - return nil, err - } - return patients, nil -} diff --git a/sandbox/go.mod b/sandbox/go.mod deleted file mode 100644 index 7fff5e7..0000000 --- a/sandbox/go.mod +++ /dev/null @@ -1,25 +0,0 @@ -module sandbox - -go 1.23.0 - -require ( - github.com/google/uuid v1.6.0 - github.com/maddalax/htmgo/framework v0.0.0-20240914010415-2397bf9fb057 - github.com/maddalax/htmgo/framework-ui v0.0.0-20240914003619-c256552b2143 - github.com/redis/go-redis/v9 v9.6.1 -) - -require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/klauspost/compress v1.17.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.25.0 // indirect -) diff --git a/sandbox/go.sum b/sandbox/go.sum deleted file mode 100644 index ae73e3b..0000000 --- a/sandbox/go.sum +++ /dev/null @@ -1,34 +0,0 @@ -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/labstack/echo/v4 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= -github.com/labstack/echo/v4 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/maddalax/htmgo/framework v0.0.0-20240914010415-2397bf9fb057 h1:DTc3qPMvwrh6wPIusGmQ1jPxngy7T6maUDh5RwYf6H0= -github.com/maddalax/htmgo/framework v0.0.0-20240914010415-2397bf9fb057/go.mod h1:O8ogYQjCn5iD9CzjdRgfrJfP9uMpx8rrx6YD5N4AecY= -github.com/maddalax/htmgo/framework-ui v0.0.0-20240914003619-c256552b2143 h1:aGpoab8N9rzXJAHIP8WiW5CsHjpSCrGlnbd0bQCRS3g= -github.com/maddalax/htmgo/framework-ui v0.0.0-20240914003619-c256552b2143/go.mod h1:Z8Mym1vqAMNcgtu1sHbWwQIwTvtNr6VqRZhKBweiCNE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= -github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/sandbox/main.go b/sandbox/main.go deleted file mode 100644 index 52b9cec..0000000 --- a/sandbox/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "github.com/google/uuid" - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" - "log" - "starter-template/pages" - "starter-template/partials/load" - "time" -) - -func main() { - f := echo.New() - - f.Static("/public", "./assets/dist") - - f.Use(func(ctx echo.Context) error { - if ctx.Cookies("htmgo-session") != "" { - return ctx.Next() - } - id := ctx.IP() + uuid.NewString() - ctx.Cookie(&echo.Cookie{ - Name: "htmgo-session", - Value: id, - SessionOnly: true, - }) - return ctx.Next() - }) - - f.Use(func(ctx echo.Context) error { - if ctx.Path() == "/livereload" { - return ctx.Next() - } - now := time.Now() - err := ctx.Next() - duration := time.Since(now) - ctx.Set("X-Response-Time", duration.String()) - // Log or print the request method, URL, and duration - log.Printf("Request: %s %s took %dms", ctx.Method(), ctx.OriginalURL(), duration.Milliseconds()) - return err - }) - - load.RegisterPartials(f) - pages.RegisterPages(f) - - h.Start(f, h.App{ - LiveReload: true, - }) -} diff --git a/sandbox/news/posts.go b/sandbox/news/posts.go deleted file mode 100644 index 977406f..0000000 --- a/sandbox/news/posts.go +++ /dev/null @@ -1,58 +0,0 @@ -package news - -import ( - "fmt" - "github.com/maddalax/htmgo/framework/util/httpjson" - "sync" -) - -type Post struct { - By string `json:"by"` - Descendants int `json:"descendants"` - Id int `json:"id"` - Kids []int `json:"kids"` - Score int `json:"score"` - Time int `json:"time"` - Title string `json:"title"` - Type string `json:"type"` - Url string `json:"url"` -} - -func List() ([]Post, error) { - responseIds, err := httpjson.Get[[]int64]("https://hacker-news.firebaseio.com/v0/topstories.json") - responseIds = responseIds[0:50] - if err != nil { - return []Post{}, err - } - - var wg sync.WaitGroup - posts := make([]Post, len(responseIds)) - - for index, id := range responseIds { - wg.Add(1) - id := id - index := index - go func() { - defer wg.Done() - url := fmt.Sprintf("https://hacker-news.firebaseio.com/v0/item/%d.json", id) - post, err := httpjson.Get[Post](url) - if err != nil { - println(err.Error()) - } - posts[index] = post - }() - } - - wg.Wait() - - return posts, nil -} - -func Get(id string) (Post, error) { - url := fmt.Sprintf("https://hacker-news.firebaseio.com/v0/item/%s.json", id) - post, err := httpjson.Get[Post](url) - if err != nil { - return Post{}, err - } - return post, nil -} diff --git a/sandbox/news/views.go b/sandbox/news/views.go deleted file mode 100644 index 05a0935..0000000 --- a/sandbox/news/views.go +++ /dev/null @@ -1,44 +0,0 @@ -package news - -import ( - "fmt" - "github.com/maddalax/htmgo/framework/h" - "starter-template/database" - "time" -) - -func StoryList() h.Ren { - - posts, _ := database.GetOrSet[[]Post]("posts", func() []Post { - p, _ := List() - return p - }) - - time.Sleep(200 * time.Millisecond) - - if len(*posts) == 0 { - return h.Pf("No results found") - } - - return h.Fragment( - h.Div(h.List(*posts, func(item Post, index int) h.Ren { - return StoryCard(item) - })), - ) -} - -func StoryCard(post Post) h.Ren { - url := fmt.Sprintf("/news/%d", post.Id) - return h.Div( - h.Class("items-center bg-indigo-200 p-4 rounded"), - h.A(h.Text(post.Title), h.Href(url)), - ) -} - -func StoryFull(id string) h.Ren { - post, err := Get(id) - if err != nil { - return h.Pf(err.Error()) - } - return StoryCard(post) -} diff --git a/sandbox/pages/base/root.go b/sandbox/pages/base/root.go deleted file mode 100644 index 2b4b9b6..0000000 --- a/sandbox/pages/base/root.go +++ /dev/null @@ -1,25 +0,0 @@ -package base - -import ( - "github.com/maddalax/htmgo/framework/h" - "starter-template/partials" - "starter-template/partials/sheet" -) - -func RootPage(children ...h.Ren) h.Ren { - return h.Html( - h.HxExtension("path-deps, response-targets, mutation-error"), - h.Head( - h.Link("/public/main.css", "stylesheet"), - h.Script("/public/htmgo.js"), - ), - h.Body( - partials.NavBar(), - sheet.Closed(), - h.Div( - h.Class("flex flex-col gap-2 bg-white h-full"), - h.Fragment(children...), - ), - ), - ) -} diff --git a/sandbox/pages/generated.go b/sandbox/pages/generated.go deleted file mode 100644 index a98135a..0000000 --- a/sandbox/pages/generated.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package pages THIS FILE IS GENERATED. DO NOT EDIT. -package pages - -import "github.com/labstack/echo/v4" -import "github.com/maddalax/htmgo/framework/h" - -func RegisterPages(f *echo.Echo) { - f.Get("/", func(ctx echo.Context) error { - return h.HtmlView(ctx, IndexPage(ctx)) - }) - f.Get("/news/:id", func(ctx echo.Context) error { - return h.HtmlView(ctx, Test(ctx)) - }) - f.Get("/news", func(ctx echo.Context) error { - return h.HtmlView(ctx, ListPage(ctx)) - }) - f.Get("/patients", func(ctx echo.Context) error { - return h.HtmlView(ctx, PatientsIndex(ctx)) - }) -} diff --git a/sandbox/pages/index.go b/sandbox/pages/index.go deleted file mode 100644 index a9a0f0d..0000000 --- a/sandbox/pages/index.go +++ /dev/null @@ -1,89 +0,0 @@ -package pages - -import ( - "fmt" - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" - "os" - "time" -) - -func IndexPage(c echo.Context) *h.Page { - return h.NewPage(h.Html( - h.Class("bg-background flex flex-col items-center"), - h.Head( - h.Link("/public/main.css", "stylesheet"), - h.Script("/public/htmgo.js"), - h.Script("/public/scripts/shiki.js"), - ), - h.Body( - h.Class("flex flex-col gap-3"), - // Navbar - h.Div( - h.Class("flex justify-between items-center w-full p-6"), - h.Div( - h.Class("text-white text-xl font-bold"), - h.Text("htmgo"), - ), - h.Div( - h.Class("flex gap-4"), - h.A(h.Href("/pricing"), h.Class("text-white"), h.Text("Pricing")), - h.A(h.Href("/docs"), h.Class("text-white"), h.Text("Docs")), - h.A(h.Href("/app"), h.Class("text-white"), h.Text("App")), - ), - ), - - // Hero Section - h.Div( - h.Class("flex flex-col items-center justify-center gap-6 p-12 bg-background text-center"), - h.H1( - h.Class("text-white text-4xl sm:text-5xl font-bold max-w-3xl"), - h.Text("Go and HTMX: The Simple Stack"), - ), - h.P( - h.Class("text-white text-lg sm:text-xl max-w-2xl"), - h.Text("Combine the simplicity of Go with the power of HTMX for dynamic, JavaScript-light web development."), - ), - h.A(h.Href("/get-started"), - h.Class("bg-white text-background px-6 py-3 rounded-md font-semibold mt-4"), - h.Text("Join the waitlist"), - ), - ), - - // Explore Section - h.Div( - h.Class("w-full max-w-4xl"), - CodeExample(), - ), - - // Footer Section - h.Div( - h.Class("flex justify-center items-center py-6"), - h.Text(fmt.Sprintf("© %d htmgo", time.Now().Year())), - ), - ), - )) -} - -func CodeExample() h.Ren { - code, err := os.ReadFile("pages/assets/_example.go") - scriptSrc, err := os.ReadFile("pages/assets/shiki.js") - - if err != nil { - return h.Pf("Error loading code example") - } - - fmt.Printf("%s\n", code) - - script := fmt.Sprintf(string(scriptSrc), string(code)) - - return h.Div( - h.Class("text-white rounded-lg"), - h.Pre(h.Id("foo")), - h.RawF(` - - `, script), - ) -} diff --git a/sandbox/pages/news.$id.go b/sandbox/pages/news.$id.go deleted file mode 100644 index a3a70a9..0000000 --- a/sandbox/pages/news.$id.go +++ /dev/null @@ -1,14 +0,0 @@ -package pages - -import ( - "fmt" - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" -) - -func Test(ctx echo.Context) *h.Page { - text := fmt.Sprintf("News ID: %s", ctx.Params("id")) - return h.NewPage( - h.Div(h.Text(text)), - ) -} diff --git a/sandbox/pages/news.index.go b/sandbox/pages/news.index.go deleted file mode 100644 index c887ae7..0000000 --- a/sandbox/pages/news.index.go +++ /dev/null @@ -1,29 +0,0 @@ -package pages - -import ( - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" - "starter-template/pages/base" - "starter-template/partials" -) - -func ListPage(ctx echo.Context) *h.Page { - return h.NewPage(base.RootPage( - list(ctx), - )) -} - -func list(ctx echo.Context) h.Ren { - return h.Fragment( - h.ViewOnLoad(partials.NewsSheet), - h.Div( - h.Class("inline-flex flex-col gap-4 p-4"), - h.Div( - h.Class("max-w-md flex flex-col gap-4 "), - partials.OpenSheetButton(h.GetQueryParam(ctx, "open") == "true"), - ), - h.Div( - h.ViewOnLoad(partials.NewsSheetOpenCount), - h.Text("you opened sheet 0 times")), - )) -} diff --git a/sandbox/pages/patients.index.go b/sandbox/pages/patients.index.go deleted file mode 100644 index c9d511e..0000000 --- a/sandbox/pages/patients.index.go +++ /dev/null @@ -1,27 +0,0 @@ -package pages - -import ( - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" - "starter-template/pages/base" - "starter-template/partials/patient" -) - -func PatientsIndex(ctx echo.Context) *h.Page { - return h.NewPage(base.RootPage( - h.Div( - h.Class("flex flex-col p-4 w-full"), - h.Div( - h.Div( - h.Class("flex justify-between items-center"), - h.P(h.Text("Manage Patients"), h.Class("text-lg font-bold")), - patient.AddPatientButton(), - ), - h.View(patient.List, h.ReloadParams{ - Triggers: h.CreateTriggers("load", "path-deps"), - Children: h.Children(h.Attribute("path-deps", h.GetPartialPath(patient.Create))), - }), - ), - ), - )) -} diff --git a/sandbox/partials/button.go b/sandbox/partials/button.go deleted file mode 100644 index 7b18057..0000000 --- a/sandbox/partials/button.go +++ /dev/null @@ -1,26 +0,0 @@ -package partials - -import ( - "github.com/maddalax/htmgo/framework-ui/ui" - "github.com/maddalax/htmgo/framework/h" -) - -func OpenSheetButton(open bool, children ...h.Ren) h.Ren { - if open { - return ui.PrimaryButton(ui.ButtonProps{ - Id: "open-sheet", - Text: "Close NewsSheet", - Target: "#sheet-partial", - Get: h.GetPartialPathWithQs(NewsSheet, "open=false"), - Children: children, - }) - } else { - return ui.PrimaryButton(ui.ButtonProps{ - Id: "open-sheet", - Text: "Open NewsSheet", - Target: "#sheet-partial", - Get: h.GetPartialPathWithQs(NewsSheet, "open=true"), - Children: children, - }) - } -} diff --git a/sandbox/partials/load/generated.go b/sandbox/partials/load/generated.go deleted file mode 100644 index 5db01f4..0000000 --- a/sandbox/partials/load/generated.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package partials THIS FILE IS GENERATED. DO NOT EDIT. -package load - -import "github.com/maddalax/htmgo/framework/h" -import "github.com/labstack/echo/v4" -import "starter-template/partials" -import "starter-template/partials/patient" -import "starter-template/partials/sheet" - -func GetPartialFromContext(ctx echo.Context) *h.Partial { - path := ctx.Path() - if path == "NewsSheet" || path == "/starter-template/partials.NewsSheet" { - return partials.NewsSheet(ctx) - } - if path == "NewsSheetOpenCount" || path == "/starter-template/partials.NewsSheetOpenCount" { - return partials.NewsSheetOpenCount(ctx) - } - if path == "Create" || path == "/starter-template/partials/patient.Create" { - return patient.Create(ctx) - } - if path == "List" || path == "/starter-template/partials/patient.List" { - return patient.List(ctx) - } - if path == "AddPatientSheetPartial" || path == "/starter-template/partials/patient.AddPatientSheetPartial" { - return patient.AddPatientSheetPartial(ctx) - } - if path == "ValidateForm" || path == "/starter-template/partials/patient.ValidateForm" { - return patient.ValidateForm(ctx) - } - if path == "Close" || path == "/starter-template/partials/sheet.Close" { - return sheet.Close(ctx) - } - return nil -} - -func RegisterPartials(f *echo.Echo) { - f.All("starter-template/partials*", func(ctx echo.Context) error { - partial := GetPartialFromContext(ctx) - if partial == nil { - return ctx.SendStatus(404) - } - return h.PartialView(ctx, partial) - }) -} diff --git a/sandbox/partials/nav.go b/sandbox/partials/nav.go deleted file mode 100644 index 68af16c..0000000 --- a/sandbox/partials/nav.go +++ /dev/null @@ -1,25 +0,0 @@ -package partials - -import "github.com/maddalax/htmgo/framework/h" - -type Link struct { - Name string - Path string -} - -func NavBar() h.Ren { - - links := []Link{ - {"Home", "/"}, - {"News", "/news"}, - {"Patients", "/patients"}, - } - - return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"), - h.Boost(), - h.Children( - h.Map(links, func(link Link) h.Ren { - return h.A(h.Text(link.Name), h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400")) - })..., - )) -} diff --git a/sandbox/partials/news.go b/sandbox/partials/news.go deleted file mode 100644 index ee588b8..0000000 --- a/sandbox/partials/news.go +++ /dev/null @@ -1,65 +0,0 @@ -package partials - -import ( - "fmt" - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework-ui/ui" - "github.com/maddalax/htmgo/framework/h" - "starter-template/news" -) - -func NewsSheet(ctx echo.Context) *h.Partial { - open := h.GetQueryParam(ctx, "open") == "true" - return h.NewPartialWithHeaders( - &map[string]string{ - "hx-trigger": "sheetOpened", - "hx-push-url": fmt.Sprintf("/news%s", h.Ternary(open, "?open=true", "")), - }, - SheetWrapper( - h.IfElseLazy(open, SheetOpen, SheetClosed), - h.OobSwap(ctx, OpenSheetButton(open)), - h.OobSwap(ctx, NewsSheetOpenCount(ctx).Root), - ), - ) -} - -func NewsSheetOpenCount(ctx echo.Context) *h.Partial { - - open := h.GetQueryParam(ctx, "open") == "true" - - return h.NewPartial(h.Div( - h.Id("sheet-open-count"), - h.IfElse(open, - h.Text(fmt.Sprintf("you opened sheet %d times", 1)), - h.Text("sheet is not open")), - ), - ) -} - -func SheetWrapper(children ...h.Ren) h.Ren { - return h.Div(h.Id("sheet-partial"), h.Fragment(children...)) -} - -func SheetClosed() h.Ren { - return h.Div() -} - -func SheetOpen() h.Ren { - return h.Fragment(h.Div( - h.Class(`fixed top-0 right-0 h-full w-96 bg-gray-100 shadow-lg z-50`), - h.Div( - h.Class("p-4 overflow-y-auto h-full w-full flex flex-col gap-4"), - h.P(h.Text("News Sheet"), - h.Class("text-lg font-bold"), - ), - h.P(h.Text("Here are the latest news stories."), - h.Class("text-sm mt-2"), - ), - ui.Button(ui.ButtonProps{ - Text: "Close NewsSheet", - Target: "#sheet-partial", - Get: h.GetPartialPathWithQs(NewsSheet, "open=false"), - }), - news.StoryList(), - ))) -} diff --git a/sandbox/partials/patient/create.go b/sandbox/partials/patient/create.go deleted file mode 100644 index 08dddf8..0000000 --- a/sandbox/partials/patient/create.go +++ /dev/null @@ -1,38 +0,0 @@ -package patient - -import ( - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" - "starter-template/features/patient" - "starter-template/partials/sheet" -) - -func Create(ctx echo.Context) *h.Partial { - name := ctx.FormValue("name") - reason := ctx.FormValue("reason-for-visit") - location := ctx.FormValue("location-name") - - err := patient.NewService(ctx).Create(patient.CreatePatientRequest{ - Name: name, - ReasonForVisit: reason, - LocationName: location, - }) - - if err != nil { - ctx.Status(500) - return h.NewPartialWithHeaders(h.NewHeaders(""), - h.Div( - h.Text("Error creating patient"), - h.Class("text-red-500"), - ), - ) - } - - headers := h.CombineHeaders(h.PushQsHeader(ctx, "adding", ""), &map[string]string{ - "HX-HxTrigger": "patient-added", - }) - - return h.NewPartialWithHeaders( - headers, - sheet.Close(ctx)) -} diff --git a/sandbox/partials/patient/patient.go b/sandbox/partials/patient/patient.go deleted file mode 100644 index fa9ef10..0000000 --- a/sandbox/partials/patient/patient.go +++ /dev/null @@ -1,143 +0,0 @@ -package patient - -import ( - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework-ui/ui" - "github.com/maddalax/htmgo/framework/h" - "starter-template/features/patient" - "starter-template/partials/sheet" - "strings" -) - -func List(ctx echo.Context) *h.Partial { - patients, err := patient.NewService(ctx).List() - - if err != nil { - return h.NewPartial(h.Div( - h.Class("patient-list"), - h.Pf("Error loading patients"), - )) - } - - if len(patients) == 0 { - return h.NewPartial(h.Div( - h.Class("patient-list"), - h.Pf("No patients found"), - )) - } - - return h.NewPartial(h.Div( - h.Class("mt-8"), - h.Id("patient-list"), - h.List(patients, Row), - )) -} - -func AddPatientSheetPartial(ctx echo.Context) *h.Partial { - closePathQs := h.GetQueryParam(ctx, "onClosePath") - return h.NewPartialWithHeaders( - h.PushQsHeader(ctx, "adding", "true"), - AddPatientSheet( - h.Ternary(closePathQs != "", closePathQs, h.CurrentPath(ctx)), - ), - ) -} - -func AddPatientSheet(onClosePath string) h.Ren { - return sheet.Opened( - sheet.Props{ - OnClosePath: onClosePath, - ClassName: "w-[400px] bg-gray-100 p-4", - Root: h.Div( - h.Class("flex flex-col gap-4"), - h.P(h.Text("Add Patient"), h.Class("text-lg font-bold")), - addPatientForm(), - ), - }) -} - -func ValidateForm(ctx echo.Context) *h.Partial { - trigger := h.GetTriggerName(ctx) - value := ctx.FormValue(trigger) - - if trigger == "name" { - if strings.ToLower(value) == "sydne" { - return h.NewPartial(h.Pf("that name is reserved")) - } - } - - if trigger == "reason-for-visit" { - if strings.ToLower(value) == "arm hurts" { - return h.NewPartial(h.Pf("lol that reason is fake")) - } - } - - if trigger == "location-name" { - if strings.ToLower(value) == "hospital" { - return h.NewPartial(h.Pf("that location is reserved")) - } - } - - return h.NewPartial(h.Fragment()) -} - -func addPatientForm() h.Ren { - return h.Form( - h.HxExtension("debug, trigger-children"), - h.Attribute("hx-target-5*", "#submit-error"), - h.Post(h.GetPartialPath(Create)), - h.Class("flex flex-col gap-2"), - ui.Input(ui.InputProps{ - Type: "text", - Label: "Name", - Name: "name", - DefaultValue: "", - ValidationPath: h.GetPartialPath(ValidateForm), - }), - ui.Input(ui.InputProps{ - Type: "text", - Label: "Reason for visit", - Name: "reason-for-visit", - ValidationPath: h.GetPartialPath(ValidateForm), - }), - ui.Input(ui.InputProps{ - Type: "date", - Label: "Appointment Date", - Name: "appointment-date", - }), - ui.Input(ui.InputProps{ - Type: "text", - Label: "Location Name", - Name: "location-name", - ValidationPath: h.GetPartialPath(ValidateForm), - }), - ui.PrimaryButton(ui.ButtonProps{ - Text: "Add Patient", - Class: "rounded p-2", - Type: "submit", - }), - h.Div( - h.Id("submit-error"), - h.Class("text-red-500"), - ), - ) -} - -func Row(patient *patient.Patient, index int) h.Ren { - return h.Div( - h.Class("flex flex-col gap-2 rounded p-4", h.Ternary(index%2 == 0, "bg-red-100", "")), - h.Pf("Name: %s", patient.Name), - h.Pf("Reason for visit: %s", patient.ReasonForVisit), - ) -} - -func AddPatientButton() h.Ren { - return ui.Button(ui.ButtonProps{ - Id: "add-patient", - Text: "Add Patient", - Class: "bg-blue-700 text-white rounded p-2 h-12", - Trigger: "qs:adding, click", - Target: sheet.Id, - Get: h.GetPartialPathWithQs(AddPatientSheetPartial, "onClosePath=/patients"), - }) -} diff --git a/sandbox/partials/sheet/sheet.go b/sandbox/partials/sheet/sheet.go deleted file mode 100644 index bb5a446..0000000 --- a/sandbox/partials/sheet/sheet.go +++ /dev/null @@ -1,47 +0,0 @@ -package sheet - -import ( - "fmt" - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" -) - -type Props struct { - ClassName string - Root h.Ren - OnClosePath string -} - -var Id = "#active-modal" - -func Opened(props Props) h.Ren { - return h.Fragment(h.Div( - h.Class(`fixed top-0 right-0 h-full shadow-lg z-50`, - h.Ternary(props.ClassName != "", props.ClassName, "w-96 bg-gray-100")), - closeButton(props), - h.Div( - props.Root, - ))) -} - -func Closed() h.Ren { - return h.Div(h.Id(Id)) -} - -func Close(ctx echo.Context) *h.Partial { - return h.NewPartialWithHeaders( - h.Ternary(ctx.Query("path") != "", h.ReplaceUrlHeader(ctx.Query("path")), h.NewHeaders()), - h.OobSwap(ctx, Closed()), - ) -} - -func closeButton(props Props) h.Ren { - return h.Div( - h.Class("absolute top-0 right-0 p-3"), - h.Button( - h.Class("text-gray-500"), - h.GetPartialWithQs(Close, fmt.Sprintf("path=%s", props.OnClosePath)), - h.Text("X"), - ), - ) -} diff --git a/todo-list/pages/base/root.go b/todo-list/pages/base/root.go index 6500995..79b125a 100644 --- a/todo-list/pages/base/root.go +++ b/todo-list/pages/base/root.go @@ -2,20 +2,11 @@ package base import ( "github.com/maddalax/htmgo/framework/h" - "strings" ) -func Extensions() string { - extensions := []string{"path-deps", "response-targets", "mutation-error"} - if h.IsDevelopment() { - extensions = append(extensions, "livereload") - } - return strings.Join(extensions, ", ") -} - func RootPage(children ...h.Ren) h.Ren { return h.Html( - h.HxExtension(Extensions()), + h.HxExtension(h.BaseExtensions()), h.Head( h.Link("/public/main.css", "stylesheet"), h.Script("/public/htmgo.js"), diff --git a/todo-list/pages/generated.go b/todo-list/pages/generated.go index f054ce1..968a9bb 100644 --- a/todo-list/pages/generated.go +++ b/todo-list/pages/generated.go @@ -6,10 +6,6 @@ import "github.com/maddalax/htmgo/framework/h" func RegisterPages(f *echo.Echo) { f.GET("/", func(ctx echo.Context) error { - cc := ctx.(*h.RequestContext) - return h.HtmlView(ctx, IndexPage(cc)) - }) - f.GET("/tasks", func(ctx echo.Context) error { cc := ctx.(*h.RequestContext) return h.HtmlView(ctx, TaskListPage(cc)) }) diff --git a/todo-list/pages/index.go b/todo-list/pages/index.go index 7795f22..65b5cac 100644 --- a/todo-list/pages/index.go +++ b/todo-list/pages/index.go @@ -1,40 +1,29 @@ package pages import ( - "github.com/labstack/echo/v4" - "github.com/maddalax/htmgo/framework/h" "todolist/pages/base" - "todolist/partials" + "todolist/partials/task" + + "github.com/maddalax/htmgo/framework/h" ) -func IndexPage(c echo.Context) *h.Page { - return h.NewPage(h.Html( - h.HxExtension(base.Extensions()), - h.Class("bg-red-200 flex flex-col items-center h-full w-full"), - h.Head( - h.Link("/public/main.css", "stylesheet"), - h.Script("/public/htmgo.js"), - ), - h.Body( - h.Class("flex flex-col gap-4"), - h.Div(h.Class("flex gap-2 mt-6"), - Button(), - Button(), - Button(), - Button(), +func TaskListPage(ctx *h.RequestContext) *h.Page { + + title := h.Div( + h.H1(h.Class("text-7xl font-extralight text-rose-500 tracking-wide"), h.Text("todos")), + ) + + return h.NewPage(base.RootPage( + h.Div( + h.Class("bg-neutral-100 min-h-screen"), + h.Div( + h.Class("flex flex-col gap-6 p-4 items-center max-w-xl mx-auto pb-12"), + title, + task.Card(ctx), + h.Children( + h.Div(h.Text("Double-click to edit a todo")), + ), ), ), )) } - -func Button() h.Ren { - return h.Button(h.Class("btn bg-green-500 p-4 rounded text-white"), - h.Text("my button"), - h.AfterRequest( - h.SetDisabled(true), - h.RemoveClass("bg-red-600"), - h.AddClass("bg-gray-500"), - ), - h.GetPartial(partials.SamplePartial), - ) -} diff --git a/todo-list/pages/tasks.go b/todo-list/pages/tasks.go deleted file mode 100644 index 65b5cac..0000000 --- a/todo-list/pages/tasks.go +++ /dev/null @@ -1,29 +0,0 @@ -package pages - -import ( - "todolist/pages/base" - "todolist/partials/task" - - "github.com/maddalax/htmgo/framework/h" -) - -func TaskListPage(ctx *h.RequestContext) *h.Page { - - title := h.Div( - h.H1(h.Class("text-7xl font-extralight text-rose-500 tracking-wide"), h.Text("todos")), - ) - - return h.NewPage(base.RootPage( - h.Div( - h.Class("bg-neutral-100 min-h-screen"), - h.Div( - h.Class("flex flex-col gap-6 p-4 items-center max-w-xl mx-auto pb-12"), - title, - task.Card(ctx), - h.Children( - h.Div(h.Text("Double-click to edit a todo")), - ), - ), - ), - )) -} diff --git a/todo-list/partials/task/task.go b/todo-list/partials/task/task.go index c0478a5..bf15268 100644 --- a/todo-list/partials/task/task.go +++ b/todo-list/partials/task/task.go @@ -314,7 +314,7 @@ func ChangeTab(ctx *h.RequestContext) *h.Partial { tab := ctx.QueryParam("tab") return h.SwapManyPartialWithHeaders(ctx, - h.PushUrlHeader(fmt.Sprintf("/tasks?tab=%s", tab)), + h.PushQsHeader(ctx, h.NewQs("tab", tab)), List(list, tab), Footer(list, tab), )