so much stuff
This commit is contained in:
parent
c0137247b7
commit
de28a6a783
13 changed files with 238 additions and 39 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="GO-242.21829.165">
|
||||
<component name="dataSourceStorageLocal" created-in="GO-242.21829.220">
|
||||
<data-source name="site.db" uuid="6b63d5bd-e451-4904-b659-21db5c54c16d">
|
||||
<database-info product="SQLite" version="3.40.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.40.1.0" dbms="SQLITE" exact-version="3.40.1" exact-driver-version="3.40">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -3,10 +3,12 @@ module mhtml
|
|||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gofiber/fiber/v2 v2.52.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/redis/go-redis/v9 v9.0.5
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/tools v0.4.0
|
||||
)
|
||||
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -4,6 +4,8 @@ github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
|||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
|
|
@ -31,6 +33,8 @@ github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7g
|
|||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
||||
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/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ func NewPageWithHttpMethod(httpMethod string, root *Node) *Page {
|
|||
}
|
||||
}
|
||||
|
||||
func WrapPartial(ctx *fiber.Ctx, cb func(ctx *fiber.Ctx) *Partial) *Node {
|
||||
return cb(ctx).Root
|
||||
}
|
||||
|
||||
func NewPartialWithHeaders(headers *Headers, root *Node) *Partial {
|
||||
return &Partial{
|
||||
Headers: headers,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func LiveReloadHandler(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func LiveReload() *Node {
|
||||
return Div(Get("/livereload"), Trigger("every 2s"))
|
||||
return Div(Get("/livereload"), Trigger("every 200ms"))
|
||||
}
|
||||
|
||||
func AddLiveReloadHandler(path string, app *fiber.App) {
|
||||
|
|
|
|||
22
main.go
22
main.go
|
|
@ -4,11 +4,9 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
"mhtml/database"
|
||||
"mhtml/h"
|
||||
"mhtml/pages"
|
||||
"mhtml/partials"
|
||||
"mhtml/partials/sheet"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -42,7 +40,7 @@ func main() {
|
|||
return err
|
||||
})
|
||||
|
||||
f.Get("/mhtml/partials*", func(ctx *fiber.Ctx) error {
|
||||
f.All("/mhtml/partials*", func(ctx *fiber.Ctx) error {
|
||||
partial := partials.GetPartialFromContext(ctx)
|
||||
if partial == nil {
|
||||
return ctx.SendStatus(404)
|
||||
|
|
@ -51,24 +49,6 @@ func main() {
|
|||
})
|
||||
|
||||
pages.RegisterPages(f)
|
||||
|
||||
f.Post("/api/patients", func(ctx *fiber.Ctx) error {
|
||||
name := ctx.FormValue("name")
|
||||
reason := ctx.FormValue("reason-for-visit")
|
||||
location := ctx.FormValue("location-name")
|
||||
|
||||
database.HSet("patients", uuid.New().String(), partials.Patient{
|
||||
Name: name,
|
||||
ReasonForVisit: reason,
|
||||
AppointmentDate: time.Now(),
|
||||
LocationName: location,
|
||||
})
|
||||
|
||||
return h.PartialViewWithHeaders(ctx, &map[string]string{
|
||||
"HX-Trigger": "patient-added",
|
||||
}, sheet.Close(ctx))
|
||||
})
|
||||
|
||||
h.Start(f, h.App{
|
||||
LiveReload: true,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"github.com/gofiber/fiber/v2"
|
||||
"mhtml/h"
|
||||
"mhtml/pages/base"
|
||||
"mhtml/partials"
|
||||
"mhtml/partials/patient"
|
||||
)
|
||||
|
||||
func PatientsIndex(ctx *fiber.Ctx) *h.Page {
|
||||
|
|
@ -15,9 +15,9 @@ func PatientsIndex(ctx *fiber.Ctx) *h.Page {
|
|||
h.Div(
|
||||
h.Class("flex justify-between items-center"),
|
||||
h.P("Manage Patients", h.Class("text-lg font-bold")),
|
||||
partials.AddPatientButton(),
|
||||
patient.AddPatientButton(),
|
||||
),
|
||||
h.ViewWithTriggers(partials.PatientList, "load", "patient-added from:body"),
|
||||
h.ViewWithTriggers(patient.List, "load", "patient-added from:body"),
|
||||
),
|
||||
),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package partials
|
|||
|
||||
import "mhtml/h"
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
import "mhtml/partials/patient"
|
||||
import "mhtml/partials/sheet"
|
||||
|
||||
func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
|
||||
|
|
@ -13,11 +14,14 @@ func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
|
|||
if path == "NewsSheetOpenCount" || path == "/mhtml/partials.NewsSheetOpenCount" {
|
||||
return NewsSheetOpenCount(ctx)
|
||||
}
|
||||
if path == "PatientList" || path == "/mhtml/partials.PatientList" {
|
||||
return PatientList(ctx)
|
||||
if path == "Create" || path == "/mhtml/partials/patient.Create" {
|
||||
return patient.Create(ctx)
|
||||
}
|
||||
if path == "AddPatientSheet" || path == "/mhtml/partials.AddPatientSheet" {
|
||||
return AddPatientSheet(ctx)
|
||||
if path == "List" || path == "/mhtml/partials/patient.List" {
|
||||
return patient.List(ctx)
|
||||
}
|
||||
if path == "AddPatientSheet" || path == "/mhtml/partials/patient.AddPatientSheet" {
|
||||
return patient.AddPatientSheet(ctx)
|
||||
}
|
||||
if path == "Close" || path == "/mhtml/partials/sheet.Close" {
|
||||
return sheet.Close(ctx)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ func NavBar() *h.Node {
|
|||
{"Patients", "/patients"},
|
||||
}
|
||||
|
||||
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"), h.Children(h.Map(links, func(link Link) *h.Node {
|
||||
return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
|
||||
}),
|
||||
))
|
||||
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"),
|
||||
h.Children(
|
||||
h.Map(links, func(link Link) *h.Node {
|
||||
return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
29
partials/patient/create.go
Normal file
29
partials/patient/create.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package patient
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
"mhtml/database"
|
||||
"mhtml/h"
|
||||
"mhtml/partials/sheet"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Create(ctx *fiber.Ctx) *h.Partial {
|
||||
name := ctx.FormValue("name")
|
||||
reason := ctx.FormValue("reason-for-visit")
|
||||
location := ctx.FormValue("location-name")
|
||||
|
||||
database.HSet("patients", uuid.New().String(), Patient{
|
||||
Name: name,
|
||||
ReasonForVisit: reason,
|
||||
AppointmentDate: time.Now(),
|
||||
LocationName: location,
|
||||
})
|
||||
|
||||
headers := &map[string]string{
|
||||
"HX-Trigger": "patient-added",
|
||||
}
|
||||
|
||||
return h.NewPartialWithHeaders(headers, h.WrapPartial(ctx, sheet.Close))
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package partials
|
||||
package patient
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
|
@ -16,7 +16,7 @@ type Patient struct {
|
|||
LocationName string
|
||||
}
|
||||
|
||||
func PatientList(ctx *fiber.Ctx) *h.Partial {
|
||||
func List(ctx *fiber.Ctx) *h.Partial {
|
||||
patients, err := database.HList[Patient]("patients")
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -36,7 +36,7 @@ func PatientList(ctx *fiber.Ctx) *h.Partial {
|
|||
return h.NewPartial(h.Div(
|
||||
h.Class("mt-8"),
|
||||
h.Id("patient-list"),
|
||||
h.List(patients, PatientRow),
|
||||
h.List(patients, Row),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +54,7 @@ func AddPatientSheet(ctx *fiber.Ctx) *h.Partial {
|
|||
|
||||
func addPatientForm() *h.Node {
|
||||
return h.Form(
|
||||
h.Post("/api/patients"),
|
||||
h.Post(h.GetPartialPath(Create)),
|
||||
h.Class("flex flex-col gap-2"),
|
||||
ui.Input(ui.InputProps{
|
||||
Type: "text",
|
||||
|
|
@ -84,7 +84,7 @@ func addPatientForm() *h.Node {
|
|||
)
|
||||
}
|
||||
|
||||
func PatientRow(patient *Patient, index int) *h.Node {
|
||||
func Row(patient *Patient, index int) *h.Node {
|
||||
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),
|
||||
55
sandbox.go
Normal file
55
sandbox.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package main
|
||||
|
||||
import "mhtml/h"
|
||||
|
||||
func init() {
|
||||
h.Div(
|
||||
h.Id("sandbox"),
|
||||
h.Button(
|
||||
h.Id("btn"),
|
||||
h.Class("bg-blue-500 text-white p-2"),
|
||||
),
|
||||
)
|
||||
|
||||
h.Html(
|
||||
h.Head(),
|
||||
h.Body(
|
||||
h.Nav(
|
||||
h.Class("flex gap-4 items-center p-4 text-slate-600 "),
|
||||
h.A(h.Href("/"), h.Class("cursor-pointer hover:text-blue-400 ")),
|
||||
h.A(h.Class("cursor-pointer hover:text-blue-400 "),
|
||||
h.Href("/news")), h.A(
|
||||
h.Href("/patients"),
|
||||
h.Class("cursor-pointer hover:text-blue-400 "))),
|
||||
h.Div(
|
||||
h.Id("active-modal")),
|
||||
h.Div(h.Class("flex flex-col gap-2 bg-white h-full "),
|
||||
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.Class("text-lg font-bold ")),
|
||||
h.Button(h.HxTarget("#active-modal"),
|
||||
h.Type("button"),
|
||||
h.Id("add-patient"),
|
||||
h.Class("flex gap-1 items-center border p-4 rounded cursor-hover bg-blue-700 text-white rounded p-2 h-12 "),
|
||||
h.HxGet("mhtml/partials/patient.AddPatientSheet"))),
|
||||
h.Div(h.HxGet("mhtml/partials/patient.List"),
|
||||
h.HxTrigger("load, patient-added from:body"),
|
||||
h.Class(""), h.Div(h.Class("mt-8"),
|
||||
h.Id("patient-list"),
|
||||
h.Div(h.Class("flex flex-col gap-2 rounded p-4 bg-red-100 "),
|
||||
h.P(),
|
||||
h.P()),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.HxGet("/livereload"),
|
||||
h.HxTrigger("every 200ms"),
|
||||
h.Class(""),
|
||||
),
|
||||
))
|
||||
|
||||
}
|
||||
119
tooling/htmltogo/entry.go
Normal file
119
tooling/htmltogo/entry.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/dave/jennifer/jen"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Example HTML input
|
||||
htmlData := `
|
||||
<body><nav class="flex gap-4 items-center p-4 text-slate-600 "><a href="/" class="cursor-pointer hover:text-blue-400 ">Home</a><a class="cursor-pointer hover:text-blue-400 " href="/news">News</a><a href="/patients" class="cursor-pointer hover:text-blue-400 ">Patients</a></nav><div id="active-modal"></div><div class="flex flex-col gap-2 bg-white h-full "><div class="flex flex-col p-4 w-full "><div><div class="flex justify-between items-center "><p class="text-lg font-bold ">Manage Patients</p><button hx-target="#active-modal" type="button" id="add-patient" class="flex gap-1 items-center border p-4 rounded cursor-hover bg-blue-700 text-white rounded p-2 h-12 " hx-get="mhtml/partials/patient.AddPatientSheet">Add Patient</button></div><div hx-get="mhtml/partials/patient.List" hx-trigger="load, patient-added from:body" class=""><div class="mt-8" id="patient-list"><div class="flex flex-col gap-2 rounded p-4 bg-red-100 "><p>Name: Sydne</p><p>Reason for visit: arm hurts</p></div></div></div></div></div></div><div hx-get="/livereload" hx-trigger="every 200ms" class=""></div></body>
|
||||
`
|
||||
|
||||
// Parse the HTML
|
||||
doc, err := html.Parse(bytes.NewReader([]byte(htmlData)))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a new Jennifer file
|
||||
f := jen.NewFile("main")
|
||||
|
||||
// Generate Jennifer code for the parsed HTML tree
|
||||
generatedCode := processNode(doc.FirstChild)
|
||||
|
||||
// Add the generated code to the file
|
||||
f.Func().Id("Render").Params().Block(generatedCode...)
|
||||
|
||||
// Render the generated code
|
||||
var buf bytes.Buffer
|
||||
err = f.Render(&buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
//// Format the generated code
|
||||
//formattedCode, err := format.Source(buf.Bytes())
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
|
||||
// Output the formatted code
|
||||
fmt.Println(string(buf.Bytes()))
|
||||
}
|
||||
|
||||
// Recursively process the HTML nodes and generate Jennifer code
|
||||
func processNode(n *html.Node) []jen.Code {
|
||||
var code []jen.Code
|
||||
|
||||
// Only process element nodes
|
||||
if n.Type == html.ElementNode {
|
||||
// Create a dynamic method call based on the tag name
|
||||
tagMethod := strings.Title(n.Data) // Capitalize the first letter of the tag
|
||||
|
||||
// Add dynamic method call for the tag (e.g., h.Div(), h.Button(), etc.)
|
||||
code = append(code, jen.Id("h").Dot(tagMethod).Call(mergeArgs(n)...))
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
// Merge attributes and children into a single slice for Call()
|
||||
func mergeArgs(n *html.Node) []jen.Code {
|
||||
// Process attributes
|
||||
attrs := processAttributes(n.Attr)
|
||||
|
||||
// Process children
|
||||
children := processChildren(n)
|
||||
|
||||
// Combine attributes and children into one slice
|
||||
return append(attrs, children...)
|
||||
}
|
||||
|
||||
// Process child nodes of a given HTML node
|
||||
func processChildren(n *html.Node) []jen.Code {
|
||||
var children []jen.Code
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
children = append(children, processNode(c)...)
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
func FormatFieldName(name string) string {
|
||||
split := strings.Split(name, "_")
|
||||
if strings.Contains(name, "-") {
|
||||
split = strings.Split(name, "-")
|
||||
}
|
||||
parts := make([]string, 0)
|
||||
for _, s := range split {
|
||||
parts = append(parts, PascalCase(s))
|
||||
}
|
||||
return strings.Join(parts, "")
|
||||
}
|
||||
|
||||
func PascalCase(s string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
// Convert the first rune (character) to uppercase and concatenate with the rest of the string
|
||||
return strings.ToUpper(string(s[0])) + s[1:]
|
||||
}
|
||||
|
||||
// Process the attributes of an HTML node and return Jennifer code
|
||||
func processAttributes(attrs []html.Attribute) []jen.Code {
|
||||
var args []jen.Code
|
||||
for _, attr := range attrs {
|
||||
// Dynamically handle all attributes
|
||||
attrMethod := FormatFieldName(attr.Key) // E.g., convert "data-role" to "DataRole"
|
||||
args = append(args, jen.Id("h").Dot(attrMethod).Call(jen.Lit(attr.Val)))
|
||||
}
|
||||
return args
|
||||
}
|
||||
Loading…
Reference in a new issue