Skip to content

Commit

Permalink
htmgo - custom formatter (#47)
Browse files Browse the repository at this point in the history
* format htmgo elements on save

* formatter updates

* ensure we maintain comments
  • Loading branch information
maddalax authored Oct 25, 2024
1 parent 3f8ab7d commit 8736c00
Show file tree
Hide file tree
Showing 35 changed files with 762 additions and 305 deletions.
27 changes: 24 additions & 3 deletions cli/htmgo/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/maddalax/htmgo/cli/htmgo/tasks/copyassets"
"github.com/maddalax/htmgo/cli/htmgo/tasks/css"
"github.com/maddalax/htmgo/cli/htmgo/tasks/downloadtemplate"
"github.com/maddalax/htmgo/cli/htmgo/tasks/formatter"
"github.com/maddalax/htmgo/cli/htmgo/tasks/process"
"github.com/maddalax/htmgo/cli/htmgo/tasks/reloader"
"github.com/maddalax/htmgo/cli/htmgo/tasks/run"
Expand All @@ -19,10 +20,10 @@ import (
)

func main() {
done := RegisterSignals()
needsSignals := true

commandMap := make(map[string]*flag.FlagSet)
commands := []string{"template", "run", "watch", "build", "setup", "css", "schema", "generate"}
commands := []string{"template", "run", "watch", "build", "setup", "css", "schema", "generate", "format"}

for _, command := range commands {
commandMap[command] = flag.NewFlagSet(command, flag.ExitOnError)
Expand Down Expand Up @@ -56,6 +57,15 @@ func main() {
slog.Debug("Running task:", slog.String("task", taskName))
slog.Debug("working dir:", slog.String("dir", process.GetWorkingDir()))

if taskName == "format" {
needsSignals = false
}

done := make(chan bool, 1)
if needsSignals {
done = RegisterSignals()
}

if taskName == "watch" {
fmt.Printf("Running in watch mode\n")
os.Setenv("ENV", "development")
Expand Down Expand Up @@ -90,7 +100,18 @@ func main() {
}()
startWatcher(reloader.OnFileChange)
} else {
if taskName == "schema" {
if taskName == "format" {
if len(os.Args) < 3 {
fmt.Println(fmt.Sprintf("Usage: htmgo format <file>"))
os.Exit(1)
}
file := os.Args[2]
if file == "." {
formatter.FormatDir(process.GetWorkingDir())
} else {
formatter.FormatFile(os.Args[2])
}
} else if taskName == "schema" {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter entity name:")
text, _ := reader.ReadString('\n')
Expand Down
50 changes: 50 additions & 0 deletions cli/htmgo/tasks/formatter/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package formatter

import (
"fmt"
"github.com/maddalax/htmgo/tools/html-to-htmgo/htmltogo"
"os"
"path/filepath"
"strings"
)

func FormatDir(dir string) {
files, err := os.ReadDir(dir)
if err != nil {
fmt.Printf("error reading dir: %s\n", err.Error())
return
}
for _, file := range files {
if file.IsDir() {
FormatDir(filepath.Join(dir, file.Name()))
} else {
FormatFile(filepath.Join(dir, file.Name()))
}
}
}

func FormatFile(file string) {
if !strings.HasSuffix(file, ".go") {
return
}

fmt.Printf("formatting file: %s\n", file)

source, err := os.ReadFile(file)
if err != nil {
fmt.Printf("error reading file: %s\n", err.Error())
return
}

str := string(source)

if !strings.Contains(str, "github.com/maddalax/htmgo/framework/h") {
return
}

parsed := htmltogo.Indent(str)

os.WriteFile(file, []byte(parsed), 0644)

return
}
2 changes: 1 addition & 1 deletion cli/htmgo/tasks/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func OnShutdown() {
}
}
// give it a second
time.Sleep(time.Second * 2)
time.Sleep(time.Second * 1)
// force kill
KillAll()
}
Expand Down
15 changes: 11 additions & 4 deletions examples/chat/chat/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,25 @@ import (
func MessageRow(message *Message) *h.Element {
return h.Div(
h.Attribute("hx-swap-oob", "beforeend"),
h.Class("flex flex-col gap-4 w-full break-words whitespace-normal"), // Ensure container breaks long words
h.Class("flex flex-col gap-4 w-full break-words whitespace-normal"),
// Ensure container breaks long words
h.Id("messages"),
h.Div(
h.Class("flex flex-col gap-1"),
h.Div(
h.Class("flex gap-2 items-center"),
h.Pf(message.UserName, h.Class("font-bold")),
h.Pf(
message.UserName,
h.Class("font-bold"),
),
h.Pf(message.CreatedAt.In(time.Local).Format("01/02 03:04 PM")),
),
h.Article(
h.Class("break-words whitespace-normal"), // Ensure message text wraps correctly
h.P(h.Text(message.Message)),
h.Class("break-words whitespace-normal"),
// Ensure message text wraps correctly
h.P(
h.Text(message.Message),
),
),
),
)
Expand Down
5 changes: 4 additions & 1 deletion examples/chat/components/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ func FormError(error string) *h.Element {
return h.Div(
h.Id("form-error"),
h.Text(error),
h.If(error != "", h.Class("p-4 bg-rose-400 text-white rounded")),
h.If(
error != "",
h.Class("p-4 bg-rose-400 text-white rounded"),
),
)
}
50 changes: 38 additions & 12 deletions examples/chat/components/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ type InputProps struct {
}

func Input(props InputProps) *h.Element {
validation := h.If(props.ValidationPath != "", h.Children(
h.Post(props.ValidationPath, hx.BlurEvent),
h.Attribute("hx-swap", "innerHTML transition:true"),
h.Attribute("hx-target", "next div"),
))
validation := h.If(
props.ValidationPath != "",
h.Children(
h.Post(props.ValidationPath, hx.BlurEvent),
h.Attribute("hx-swap", "innerHTML transition:true"),
h.Attribute("hx-target", "next div"),
),
)

if props.Type == "" {
props.Type = "text"
Expand All @@ -32,18 +35,41 @@ func Input(props InputProps) *h.Element {
input := h.Input(
props.Type,
h.Class("border p-2 rounded focus:outline-none focus:ring focus:ring-slate-800"),
h.If(props.Name != "", h.Name(props.Name)),
h.If(props.Children != nil, h.Children(props.Children...)),
h.If(props.Required, h.Required()),
h.If(props.Placeholder != "", h.Placeholder(props.Placeholder)),
h.If(props.DefaultValue != "", h.Attribute("value", props.DefaultValue)),
h.If(
props.Name != "",
h.Name(props.Name),
),
h.If(
props.Children != nil,
h.Children(props.Children...),
),
h.If(
props.Required,
h.Required(),
),
h.If(
props.Placeholder != "",
h.Placeholder(props.Placeholder),
),
h.If(
props.DefaultValue != "",
h.Attribute("value", props.DefaultValue),
),
validation,
)

wrapped := h.Div(
h.If(props.Id != "", h.Id(props.Id)),
h.If(
props.Id != "",
h.Id(props.Id),
),
h.Class("flex flex-col gap-1"),
h.If(props.Label != "", h.Label(h.Text(props.Label))),
h.If(
props.Label != "",
h.Label(
h.Text(props.Label),
),
),
input,
h.Div(
h.Id(props.Id+"-error"),
Expand Down
33 changes: 16 additions & 17 deletions examples/chat/pages/chat.$id.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@ func ChatRoom(ctx *h.RequestContext) *h.Page {
RootPage(
h.Div(
h.TriggerChildren(),

h.Attribute("sse-connect", fmt.Sprintf("/sse/chat/%s", roomId)),

h.HxOnSseOpen(
js.ConsoleLog("Connected to chat room"),
),

h.HxOnSseError(
js.EvalJs(fmt.Sprintf(`
const reason = e.detail.event.data
Expand All @@ -38,35 +35,27 @@ func ChatRoom(ctx *h.RequestContext) *h.Page {
}
`, roomId, roomId)),
),

// Adjusted flex properties for responsive layout
h.Class("flex flex-row h-screen bg-neutral-100 overflow-x-hidden"),

// Collapse Button for mobile
CollapseButton(),

// Sidebar for connected users
UserSidebar(),

h.Div(
// Adjusted to fill height and width
h.Class("flex flex-col h-full w-full bg-white p-4 overflow-hidden"),

// Room name at the top, fixed
CachedRoomHeader(ctx),

h.HxAfterSseMessage(
js.EvalJsOnSibling("#messages",
`element.scrollTop = element.scrollHeight;`),
),

// Chat Messages
h.Div(
h.Id("messages"),
// Adjusted flex properties and removed max-width
h.Class("flex flex-col gap-4 mb-4 overflow-auto flex-grow w-full pt-[50px]"),
),

// Chat Input at the bottom
Form(),
),
Expand All @@ -91,7 +80,10 @@ func roomNameHeader(ctx *h.RequestContext) *h.Element {
}
return h.Div(
h.Class("bg-neutral-700 text-white p-3 shadow-sm w-full fixed top-0 left-0 flex justify-center z-10"),
h.H2F(room.Name, h.Class("text-lg font-bold")),
h.H2F(
room.Name,
h.Class("text-lg font-bold"),
),
h.Div(
h.Class("absolute right-5 top-3 cursor-pointer"),
h.Text("Share"),
Expand All @@ -108,7 +100,10 @@ func UserSidebar() *h.Element {
return h.Div(
h.Class("sidebar h-full pt-[67px] min-w-48 w-48 bg-neutral-200 p-4 flex-col justify-between gap-3 rounded-l-lg hidden md:flex"),
h.Div(
h.H3F("Connected Users", h.Class("text-lg font-bold")),
h.H3F(
"Connected Users",
h.Class("text-lg font-bold"),
),
chat.ConnectedUsers(make([]db.User, 0), ""),
),
h.A(
Expand All @@ -121,23 +116,27 @@ func UserSidebar() *h.Element {

func CollapseButton() *h.Element {
return h.Div(
h.Class("fixed top-0 left-4 md:hidden z-50"), // Always visible on mobile
h.Class("fixed top-0 left-4 md:hidden z-50"),
// Always visible on mobile
h.Button(
h.Class("p-2 text-2xl bg-neutral-700 text-white rounded-md"), // Styling the button
h.Class("p-2 text-2xl bg-neutral-700 text-white rounded-md"),
// Styling the button
h.OnClick(
js.EvalJs(`
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('hidden');
sidebar.classList.toggle('flex');
`),
),
h.UnsafeRaw("&#9776;"), // The icon for collapsing the sidebar
h.UnsafeRaw("&#9776;"),
// The icon for collapsing the sidebar
),
)
}

func MessageInput() *h.Element {
return h.Input("text",
return h.Input(
"text",
h.Id("message-input"),
h.Required(),
h.Class("p-4 rounded-md border border-slate-200 w-full focus:outline-none focus:ring focus:ring-slate-200"),
Expand Down
25 changes: 14 additions & 11 deletions examples/chat/pages/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page {
h.Class("flex flex-col items-center justify-center min-h-screen bg-neutral-100"),
h.Div(
h.Class("bg-white p-8 rounded-lg shadow-lg w-full max-w-md"),
h.H2F("htmgo chat", h.Class("text-3xl font-bold text-center mb-6")),
h.H2F(
"htmgo chat",
h.Class("text-3xl font-bold text-center mb-6"),
),
h.Form(
h.Attribute("hx-swap", "none"),
h.PostPartial(partials.CreateOrJoinRoom),
h.Class("flex flex-col gap-6"),

// Username input at the top
components.Input(components.InputProps{
Id: "username",
Expand All @@ -30,11 +32,9 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page {
h.MaxLength(15),
},
}),

// Single box for Create or Join a Chat Room
h.Div(
h.Class("p-4 border border-gray-300 rounded-md flex flex-col gap-6"),

// Create New Chat Room input
components.Input(components.InputProps{
Name: "new-chat-room",
Expand All @@ -45,15 +45,20 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page {
h.MaxLength(20),
},
}),

// OR divider
h.Div(
h.Class("flex items-center justify-center gap-4"),
h.Div(h.Class("border-t border-gray-300 flex-grow")),
h.P(h.Text("OR"), h.Class("text-gray-500")),
h.Div(h.Class("border-t border-gray-300 flex-grow")),
h.Div(
h.Class("border-t border-gray-300 flex-grow"),
),
h.P(
h.Text("OR"),
h.Class("text-gray-500"),
),
h.Div(
h.Class("border-t border-gray-300 flex-grow"),
),
),

// Join Chat Room input
components.Input(components.InputProps{
Id: "join-chat-room",
Expand All @@ -67,10 +72,8 @@ func ChatAppFirstScreen(ctx *h.RequestContext) *h.Page {
},
}),
),

// Error message
components.FormError(""),

// Submit button at the bottom
components.PrimaryButton(components.ButtonProps{
Type: "submit",
Expand Down
Loading

0 comments on commit 8736c00

Please sign in to comment.