init go project
This commit is contained in:
commit
5dde43e409
85 changed files with 16720 additions and 0 deletions
325
internal/ui/components/selectbox/selectbox.templ
Normal file
325
internal/ui/components/selectbox/selectbox.templ
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
// templui component selectbox - version: v0.101.0 installed by templui v0.101.0
|
||||
// 📚 Documentation: https://templui.io/docs/components/select-box
|
||||
package selectbox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/button"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/icon"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/input"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/popover"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
var contentIDKey contextKey = "contentID"
|
||||
|
||||
type Props struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
Multiple bool
|
||||
}
|
||||
|
||||
type TriggerProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
Name string
|
||||
Form string
|
||||
Disabled bool
|
||||
HasError bool
|
||||
Multiple bool
|
||||
ShowPills bool
|
||||
SelectedCountText string
|
||||
}
|
||||
|
||||
type ValueProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
Placeholder string
|
||||
Multiple bool
|
||||
}
|
||||
|
||||
type ContentProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
NoSearch bool
|
||||
SearchPlaceholder string
|
||||
}
|
||||
|
||||
type GroupProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
}
|
||||
|
||||
type LabelProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
}
|
||||
|
||||
type ItemProps struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
Value string
|
||||
Selected bool
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
templ SelectBox(props ...Props) {
|
||||
{{
|
||||
var p Props
|
||||
if len(props) > 0 {
|
||||
p = props[0]
|
||||
}
|
||||
wrapperID := p.ID
|
||||
if wrapperID == "" {
|
||||
wrapperID = utils.RandomID()
|
||||
}
|
||||
contentID := fmt.Sprintf("%s-content", wrapperID)
|
||||
ctx = context.WithValue(ctx, contentIDKey, contentID)
|
||||
}}
|
||||
<div
|
||||
id={ wrapperID }
|
||||
class={ utils.TwMerge("select-container w-full relative", p.Class) }
|
||||
{ p.Attributes... }
|
||||
>
|
||||
{ children... }
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Trigger(props ...TriggerProps) {
|
||||
{{
|
||||
var p TriggerProps
|
||||
if len(props) > 0 {
|
||||
p = props[0]
|
||||
}
|
||||
contentID, ok := ctx.Value(contentIDKey).(string)
|
||||
if !ok {
|
||||
contentID = "fallback-select-content-id"
|
||||
}
|
||||
if p.ShowPills {
|
||||
p.Multiple = true
|
||||
}
|
||||
}}
|
||||
@popover.Trigger(popover.TriggerProps{
|
||||
For: contentID,
|
||||
TriggerType: popover.TriggerTypeClick,
|
||||
}) {
|
||||
@button.Button(button.Props{
|
||||
ID: p.ID,
|
||||
Type: "button",
|
||||
Variant: button.VariantOutline,
|
||||
Class: utils.TwMerge(
|
||||
// Required class for JavaScript
|
||||
"select-trigger",
|
||||
// Base styles matching input
|
||||
"w-full h-9 px-3 py-1 text-base md:text-sm",
|
||||
"flex items-center justify-between",
|
||||
"rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] outline-none",
|
||||
// Dark mode background
|
||||
"dark:bg-input/30",
|
||||
// Selection styles
|
||||
"selection:bg-primary selection:text-primary-foreground",
|
||||
// Focus styles
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
// Error/Invalid styles
|
||||
"aria-invalid:ring-destructive/20 aria-invalid:border-destructive dark:aria-invalid:ring-destructive/40",
|
||||
utils.If(p.HasError, "border-destructive ring-destructive/20 dark:ring-destructive/40"),
|
||||
p.Class,
|
||||
),
|
||||
Disabled: p.Disabled,
|
||||
Attributes: utils.MergeAttributes(
|
||||
templ.Attributes{
|
||||
"data-tui-selectbox-content-id": contentID,
|
||||
"data-tui-selectbox-multiple": strconv.FormatBool(p.Multiple),
|
||||
"data-tui-selectbox-show-pills": strconv.FormatBool(p.ShowPills),
|
||||
"data-tui-selectbox-selected-count-text": p.SelectedCountText,
|
||||
"tabindex": "0",
|
||||
"aria-invalid": utils.If(p.HasError, "true"),
|
||||
},
|
||||
),
|
||||
}) {
|
||||
<input
|
||||
type="hidden"
|
||||
if p.Name != "" {
|
||||
name={ p.Name }
|
||||
}
|
||||
if p.Form != "" {
|
||||
form={ p.Form }
|
||||
}
|
||||
{ p.Attributes... }
|
||||
/>
|
||||
{ children... }
|
||||
<span class="pointer-events-none ml-1">
|
||||
@icon.ChevronDown(icon.Props{
|
||||
Size: 16,
|
||||
Class: "text-muted-foreground",
|
||||
})
|
||||
</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
templ Value(props ...ValueProps) {
|
||||
{{ var p ValueProps }}
|
||||
if len(props) > 0 {
|
||||
{{ p = props[0] }}
|
||||
}
|
||||
<span
|
||||
if p.ID != "" {
|
||||
id={ p.ID }
|
||||
}
|
||||
class={ utils.TwMerge("block truncate select-value text-muted-foreground", p.Class) }
|
||||
if p.Placeholder != "" {
|
||||
data-tui-selectbox-placeholder={ p.Placeholder }
|
||||
}
|
||||
{ p.Attributes... }
|
||||
>
|
||||
if p.Placeholder != "" {
|
||||
{ p.Placeholder }
|
||||
}
|
||||
{ children... }
|
||||
</span>
|
||||
}
|
||||
|
||||
templ Content(props ...ContentProps) {
|
||||
{{
|
||||
var p ContentProps
|
||||
if len(props) > 0 {
|
||||
p = props[0]
|
||||
}
|
||||
contentID, ok := ctx.Value(contentIDKey).(string)
|
||||
if !ok {
|
||||
contentID = "fallback-select-content-id"
|
||||
}
|
||||
}}
|
||||
@popover.Content(popover.ContentProps{
|
||||
ID: contentID,
|
||||
Placement: popover.PlacementBottomStart,
|
||||
Offset: 4,
|
||||
MatchWidth: true,
|
||||
DisableESC: !p.NoSearch,
|
||||
Class: utils.TwMerge(
|
||||
"p-1 select-content z-50 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
|
||||
"min-w-[var(--popover-trigger-width)] w-[var(--popover-trigger-width)]",
|
||||
p.Class,
|
||||
),
|
||||
Attributes: utils.MergeAttributes(
|
||||
templ.Attributes{
|
||||
"role": "listbox",
|
||||
"tabindex": "-1",
|
||||
},
|
||||
p.Attributes,
|
||||
),
|
||||
Exclusive: true,
|
||||
}) {
|
||||
if !p.NoSearch {
|
||||
<div class="sticky top-0 bg-popover p-1">
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground z-10 pointer-events-none">
|
||||
@icon.Search(icon.Props{Size: 16})
|
||||
</span>
|
||||
@input.Input(input.Props{
|
||||
Type: input.TypeSearch,
|
||||
Class: "pl-8",
|
||||
Placeholder: utils.IfElse(p.SearchPlaceholder != "", p.SearchPlaceholder, "Search..."),
|
||||
Attributes: templ.Attributes{
|
||||
"data-tui-selectbox-search": "",
|
||||
},
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="max-h-[300px] overflow-y-auto">
|
||||
{ children... }
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
templ Group(props ...GroupProps) {
|
||||
{{ var p GroupProps }}
|
||||
if len(props) > 0 {
|
||||
{{ p = props[0] }}
|
||||
}
|
||||
<div
|
||||
if p.ID != "" {
|
||||
id={ p.ID }
|
||||
}
|
||||
class={ utils.TwMerge("p-1", p.Class) }
|
||||
role="group"
|
||||
{ p.Attributes... }
|
||||
>
|
||||
{ children... }
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Label(props ...LabelProps) {
|
||||
{{ var p LabelProps }}
|
||||
if len(props) > 0 {
|
||||
{{ p = props[0] }}
|
||||
}
|
||||
<span
|
||||
if p.ID != "" {
|
||||
id={ p.ID }
|
||||
}
|
||||
class={ utils.TwMerge("px-2 py-1.5 text-sm font-medium", p.Class) }
|
||||
{ p.Attributes... }
|
||||
>
|
||||
{ children... }
|
||||
</span>
|
||||
}
|
||||
|
||||
templ Item(props ...ItemProps) {
|
||||
{{ var p ItemProps }}
|
||||
if len(props) > 0 {
|
||||
{{ p = props[0] }}
|
||||
}
|
||||
<div
|
||||
if p.ID != "" {
|
||||
id={ p.ID }
|
||||
}
|
||||
class={
|
||||
utils.TwMerge(
|
||||
"select-item relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 px-2 text-sm font-light outline-none",
|
||||
"hover:bg-accent hover:text-accent-foreground",
|
||||
"focus:bg-accent focus:text-accent-foreground",
|
||||
utils.If(p.Selected, "bg-accent text-accent-foreground"),
|
||||
utils.If(p.Disabled, "pointer-events-none opacity-50"),
|
||||
p.Class,
|
||||
),
|
||||
}
|
||||
role="option"
|
||||
data-tui-selectbox-value={ p.Value }
|
||||
data-tui-selectbox-selected={ strconv.FormatBool(p.Selected) }
|
||||
data-tui-selectbox-disabled={ strconv.FormatBool(p.Disabled) }
|
||||
tabindex="0"
|
||||
{ p.Attributes... }
|
||||
>
|
||||
<span class="truncate select-item-text">
|
||||
{ children... }
|
||||
</span>
|
||||
<span
|
||||
class={
|
||||
utils.TwMerge(
|
||||
"select-check absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
|
||||
utils.IfElse(p.Selected, "opacity-100", "opacity-0"),
|
||||
),
|
||||
}
|
||||
>
|
||||
@icon.Check(icon.Props{Size: 16})
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Script() {
|
||||
<script defer nonce={ templ.GetNonce(ctx) } src={ "/assets/js/selectbox.min.js?v=" + utils.ScriptVersion }></script>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue