Add basic web components

This commit is contained in:
Luis Eduardo Jeréz Girón
2024-07-22 00:25:56 -06:00
parent 0c1fa8551e
commit 7fe14464cd
5 changed files with 385 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
package component
import "github.com/orsinium-labs/enum"
type size enum.Member[string]
var (
SizeSm = size{"sm"}
SizeMd = size{"md"}
SizeLg = size{"lg"}
)
+56
View File
@@ -0,0 +1,56 @@
package component
import (
"github.com/maragudk/gomponents"
gcomponents "github.com/maragudk/gomponents/components"
"github.com/maragudk/gomponents/html"
)
// HxLoadingSm returns a small loading indicator.
func HxLoadingSm(centered bool, id ...string) gomponents.Node {
return hxLoading(centered, SizeSm, id...)
}
// HxLoadingMd returns a loading indicator.
func HxLoadingMd(centered bool, id ...string) gomponents.Node {
return hxLoading(centered, SizeMd, id...)
}
// HxLoadingLg returns a large loading indicator.
func HxLoadingLg(centered bool, id ...string) gomponents.Node {
return hxLoading(centered, SizeLg, id...)
}
func hxLoading(centered bool, size size, id ...string) gomponents.Node {
pickedID := ""
if len(id) > 0 {
pickedID = id[0]
}
return html.Div(
gomponents.If(
pickedID != "",
html.ID(pickedID),
),
html.Class("htmx-indicator"),
html.Div(
gcomponents.Classes{
"flex justify-center items-center": centered,
"w-full h-full": true,
},
func() gomponents.Node {
switch size {
case SizeSm:
return SpinnerSm()
case SizeMd:
return SpinnerMd()
case SizeLg:
return SpinnerLg()
default:
return SpinnerMd()
}
}(),
),
)
}
+156
View File
@@ -0,0 +1,156 @@
package component
import (
"fmt"
lucide "github.com/eduardolat/gomponents-lucide"
"github.com/eduardolat/pgbackweb/internal/view/web/alpine"
"github.com/google/uuid"
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/components"
"github.com/maragudk/gomponents/html"
)
// ModalParams are the props for the Modal component.
type ModalParams struct {
// ID is the ID of the modal dialog. If empty, a random ID will be generated.
ID string
// Content is the content of the modal dialog.
Content []gomponents.Node
// Size is the size of the modal dialog.
// Can be "sm", "md", and "lg".
// The default is "md".
Size size
// Title is the title of the modal dialog.
// If you need more than a string, use TitleNode instead.
Title string
// TitleNode is the title of the modal dialog.
// If you need only a string, use Title instead.
TitleNode gomponents.Node
// HTMXIndicator is an optional ID of an HTMX indicator that
// should be inserted in the modal header.
HTMXIndicator string
}
// ModalResult is the result of creating a modal dialog.
type ModalResult struct {
// HTML is the modal dialog HTML.
HTML gomponents.Node
// OpenerAttr is the attribute to add to the element that opens the modal dialog.
OpenerAttr gomponents.Node
}
// Modal renders a modal dialog.
func Modal(params ModalParams) ModalResult {
id := params.ID
if id == "" {
id = "mo-" + uuid.NewString()
}
openEventName := fmt.Sprintf("%s_open", id)
closeEventName := fmt.Sprintf("%s_close", id)
openerAttr := gomponents.Attr(
"onClick",
"event.preventDefault(); window.dispatchEvent(new Event('"+openEventName+"'));",
)
closerAttr := gomponents.Attr(
"onClick",
"event.preventDefault(); window.dispatchEvent(new Event('"+closeEventName+"'));",
)
openCode := `document.getElementById("` + id + `").classList.remove("hidden");`
closeCode := `document.getElementById("` + id + `").classList.add("hidden");`
size := SizeMd
if params.Size.Value != "" {
size = params.Size
}
hasHTMXIndicator := params.HTMXIndicator != ""
content := html.Div(
alpine.XData(`{}`),
alpine.XOn(fmt.Sprintf("%s.window", openEventName), openCode),
alpine.XOn(fmt.Sprintf("%s.window", closeEventName), closeCode),
alpine.XOn("keyup.escape.window", closeCode),
html.ID(id),
components.Classes{
"hidden": true,
"!p-0 !m-0 w-[100dvw] h-[100dvh]": true,
"fixed left-0 top-0 z-[1000]": true,
},
// Backdrop
html.Div(
closerAttr,
components.Classes{
"bg-black opacity-25": true,
"!w-full !h-full": true,
"z-[1001]": true,
},
),
// Dialog
html.Div(
components.Classes{
"absolute z-[1002] top-[50%] left-[50%]": true,
"translate-y-[-50%] translate-x-[-50%]": true,
"max-w-[calc(100dvw-30px)] max-h-[85dvh]": true,
"bg-base-100 rounded overflow-y-auto p-0": true,
"overflow-x-hidden whitespace-normal": true,
"w-[400px]": size == SizeSm,
"w-[600px]": size == SizeMd,
"w-[800px]": size == SizeLg,
},
html.Div(
components.Classes{
"w-full sticky top-0 right-0 bg-base-100": true,
"flex items-center justify-between": true,
"border-b border-base-300 px-4 py-3": true,
},
html.Div(
gomponents.If(
params.TitleNode != nil,
params.TitleNode,
),
gomponents.If(
params.Title != "",
html.Span(
html.Class("text-xl font-bold desk:text-2xl"),
gomponents.Text(params.Title),
),
),
gomponents.If(
hasHTMXIndicator,
html.Div(
html.Class("inline-flex h-full items-center pl-2"),
HxLoadingSm(false, params.HTMXIndicator),
),
),
),
html.Button(
html.Class("btn btn-circle btn-ghost btn-sm"),
lucide.X(html.Class("size-6")),
closerAttr,
),
),
html.Div(
html.Class("p-4"),
gomponents.Group(params.Content),
),
),
)
return ModalResult{
OpenerAttr: openerAttr,
HTML: content,
}
}
+66
View File
@@ -0,0 +1,66 @@
package component
import (
"fmt"
lucide "github.com/eduardolat/gomponents-lucide"
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/components"
"github.com/maragudk/gomponents/html"
)
func spinner(size size) gomponents.Node {
return lucide.LoaderCircle(components.Classes{
"animate-spin inline-block": true,
"size-5": size == SizeSm,
"size-8": size == SizeMd,
"size-12": size == SizeLg,
})
}
func SpinnerSm() gomponents.Node {
return spinner(SizeSm)
}
func SpinnerMd() gomponents.Node {
return spinner(SizeMd)
}
func SpinnerLg() gomponents.Node {
return spinner(SizeLg)
}
func spinnerContainer(size size, height string) gomponents.Node {
return html.Div(
components.Classes{
"flex justify-center": true,
"items-center w-full": true,
},
html.Style(fmt.Sprintf("height: %s;", height)),
spinner(size),
)
}
func SpinnerContainerSm(height ...string) gomponents.Node {
pickedHeight := "300px"
if len(height) > 0 {
pickedHeight = height[0]
}
return spinnerContainer(SizeSm, pickedHeight)
}
func SpinnerContainerMd(height ...string) gomponents.Node {
pickedHeight := "300px"
if len(height) > 0 {
pickedHeight = height[0]
}
return spinnerContainer(SizeMd, pickedHeight)
}
func SpinnerContainerLg(height ...string) gomponents.Node {
pickedHeight := "300px"
if len(height) > 0 {
pickedHeight = height[0]
}
return spinnerContainer(SizeLg, pickedHeight)
}
+96
View File
@@ -0,0 +1,96 @@
package component
import (
"github.com/maragudk/gomponents"
"github.com/maragudk/gomponents/html"
)
func H1(children ...gomponents.Node) gomponents.Node {
return html.H1(
html.Class("text-2xl font-bold desk:text-4xl"),
gomponents.Group(children),
)
}
func H2(children ...gomponents.Node) gomponents.Node {
return html.H2(
html.Class("text-xl font-bold desk:text-2xl"),
gomponents.Group(children),
)
}
func H3(children ...gomponents.Node) gomponents.Node {
return html.H3(
html.Class("text-lg font-bold desk:text-xl"),
gomponents.Group(children),
)
}
func H4(children ...gomponents.Node) gomponents.Node {
return html.H4(
html.Class("text-base font-bold desk:text-lg"),
gomponents.Group(children),
)
}
func H5(children ...gomponents.Node) gomponents.Node {
return html.H5(
html.Class("text-sm font-bold desk:text-base"),
gomponents.Group(children),
)
}
func H6(children ...gomponents.Node) gomponents.Node {
return html.H6(
html.Class("text-xs font-bold desk:text-sm"),
gomponents.Group(children),
)
}
// H1Text is a convenience function to create an H1 element with a
// simple text node as its child.
func H1Text(text string) gomponents.Node {
return H1(gomponents.Text(text))
}
// H2Text is a convenience function to create an H2 element with a
// simple text node as its child.
func H2Text(text string) gomponents.Node {
return H2(gomponents.Text(text))
}
// H3Text is a convenience function to create an H3 element with a
// simple text node as its child.
func H3Text(text string) gomponents.Node {
return H3(gomponents.Text(text))
}
// H4Text is a convenience function to create an H4 element with a
// simple text node as its child.
func H4Text(text string) gomponents.Node {
return H4(gomponents.Text(text))
}
// H5Text is a convenience function to create an H5 element with a
// simple text node as its child.
func H5Text(text string) gomponents.Node {
return H5(gomponents.Text(text))
}
// H6Text is a convenience function to create an H6 element with a
// simple text node as its child.
func H6Text(text string) gomponents.Node {
return H6(gomponents.Text(text))
}
// PText is a convenience function to create a P element with a
// simple text node as its child.
func PText(text string) gomponents.Node {
return html.P(gomponents.Text(text))
}
// SpanText is a convenience function to create a Span element with a
// simple text node as its child.
func SpanText(text string) gomponents.Node {
return html.Span(gomponents.Text(text))
}