init go project
This commit is contained in:
commit
5dde43e409
85 changed files with 16720 additions and 0 deletions
250
internal/ui/components/timepicker/timepicker.templ
Normal file
250
internal/ui/components/timepicker/timepicker.templ
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// templui component timepicker - version: v0.101.0 installed by templui v0.101.0
|
||||
// 📚 Documentation: https://templui.io/docs/components/time-picker
|
||||
package timepicker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/button"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/card"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/icon"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/ui/components/popover"
|
||||
"git.juancwu.dev/juancwu/budgething/internal/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Props struct {
|
||||
ID string
|
||||
Class string
|
||||
Attributes templ.Attributes
|
||||
Name string
|
||||
Form string
|
||||
Value time.Time
|
||||
MinTime time.Time
|
||||
MaxTime time.Time
|
||||
Step int
|
||||
Use12Hours bool
|
||||
AMLabel string
|
||||
PMLabel string
|
||||
Placeholder string
|
||||
Disabled bool
|
||||
HasError bool
|
||||
}
|
||||
|
||||
templ TimePicker(props ...Props) {
|
||||
{{
|
||||
var p Props
|
||||
if len(props) > 0 {
|
||||
p = props[0]
|
||||
}
|
||||
if p.ID == "" {
|
||||
p.ID = utils.RandomID()
|
||||
}
|
||||
if p.Name == "" {
|
||||
p.Name = p.ID
|
||||
}
|
||||
if p.Placeholder == "" {
|
||||
p.Placeholder = "Select time"
|
||||
}
|
||||
if p.AMLabel == "" {
|
||||
p.AMLabel = "AM"
|
||||
}
|
||||
if p.PMLabel == "" {
|
||||
p.PMLabel = "PM"
|
||||
}
|
||||
if p.Step <= 0 {
|
||||
p.Step = 1
|
||||
}
|
||||
|
||||
var contentID = p.ID + "-content"
|
||||
var valueString string
|
||||
if p.Value != (time.Time{}) {
|
||||
valueString = p.Value.Format("15:04")
|
||||
}
|
||||
var minTimeString string
|
||||
if p.MinTime != (time.Time{}) {
|
||||
minTimeString = p.MinTime.Format("15:04")
|
||||
}
|
||||
var maxTimeString string
|
||||
if p.MaxTime != (time.Time{}) {
|
||||
maxTimeString = p.MaxTime.Format("15:04")
|
||||
}
|
||||
}}
|
||||
<div class="relative inline-block w-full">
|
||||
<input
|
||||
type="hidden"
|
||||
name={ p.Name }
|
||||
value={ valueString }
|
||||
if p.Form != "" {
|
||||
form={ p.Form }
|
||||
}
|
||||
id={ p.ID + "-hidden" }
|
||||
data-tui-timepicker-hidden-input="true"
|
||||
/>
|
||||
@popover.Trigger(popover.TriggerProps{For: contentID}) {
|
||||
@button.Button(button.Props{
|
||||
ID: p.ID,
|
||||
Variant: button.VariantOutline,
|
||||
Class: utils.TwMerge(
|
||||
// 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(p.Attributes, templ.Attributes{
|
||||
"data-tui-timepicker": "true",
|
||||
"data-tui-timepicker-use12hours": fmt.Sprintf("%t", p.Use12Hours),
|
||||
"data-tui-timepicker-am-label": p.AMLabel,
|
||||
"data-tui-timepicker-pm-label": p.PMLabel,
|
||||
"data-tui-timepicker-placeholder": p.Placeholder,
|
||||
"data-tui-timepicker-step": fmt.Sprintf("%d", p.Step),
|
||||
"data-tui-timepicker-min-time": minTimeString,
|
||||
"data-tui-timepicker-max-time": maxTimeString,
|
||||
"aria-invalid": utils.If(p.HasError, "true"),
|
||||
}),
|
||||
}) {
|
||||
<span data-tui-timepicker-display class="text-left grow text-muted-foreground">
|
||||
{ p.Placeholder }
|
||||
</span>
|
||||
<span class="text-muted-foreground flex items-center ml-2">
|
||||
@icon.Clock(icon.Props{Size: 16})
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@popover.Content(popover.ContentProps{
|
||||
ID: contentID,
|
||||
Placement: popover.PlacementBottomStart,
|
||||
Class: "p-0 w-80",
|
||||
}) {
|
||||
@card.Card(card.Props{
|
||||
Class: "border-0 shadow-none",
|
||||
}) {
|
||||
@card.Content(card.ContentProps{
|
||||
Class: "p-4",
|
||||
}) {
|
||||
<div
|
||||
data-tui-timepicker-popup="true"
|
||||
data-tui-timepicker-input-name={ p.Name }
|
||||
data-tui-timepicker-parent-id={ p.ID }
|
||||
if valueString != "" {
|
||||
data-tui-timepicker-value={ valueString }
|
||||
}
|
||||
>
|
||||
// Time selection grid
|
||||
<div class="grid grid-cols-2 gap-3 mb-4">
|
||||
// Hour selection
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">Hour</label>
|
||||
<div class="max-h-32 overflow-y-auto border rounded-md bg-background">
|
||||
<div data-tui-timepicker-hour-list="true" class="p-1 space-y-0.5">
|
||||
if p.Use12Hours {
|
||||
// 12-hour format: 12, 01-11
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-hour="0"
|
||||
data-tui-timepicker-selected="false"
|
||||
class="w-full px-2 py-1 text-sm rounded transition-colors text-left hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-selected=true]:bg-primary data-[tui-timepicker-selected=true]:text-primary-foreground data-[tui-timepicker-selected=true]:hover:bg-primary/90"
|
||||
>
|
||||
12
|
||||
</button>
|
||||
for hour := 1; hour <= 11; hour++ {
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-hour={ strconv.Itoa(hour) }
|
||||
data-tui-timepicker-selected="false"
|
||||
class="w-full px-2 py-1 text-sm rounded transition-colors text-left hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-selected=true]:bg-primary data-[tui-timepicker-selected=true]:text-primary-foreground data-[tui-timepicker-selected=true]:hover:bg-primary/90"
|
||||
>
|
||||
{ fmt.Sprintf("%02d", hour) }
|
||||
</button>
|
||||
}
|
||||
} else {
|
||||
// 24-hour format: 00-23
|
||||
for hour := 0; hour < 24; hour++ {
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-hour={ strconv.Itoa(hour) }
|
||||
data-tui-timepicker-selected="false"
|
||||
class="w-full px-2 py-1 text-sm rounded transition-colors text-left hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-selected=true]:bg-primary data-[tui-timepicker-selected=true]:text-primary-foreground data-[tui-timepicker-selected=true]:hover:bg-primary/90"
|
||||
>
|
||||
{ fmt.Sprintf("%02d", hour) }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// Minute selection
|
||||
<div class="space-y-2">
|
||||
<label class="text-sm font-medium">Minute</label>
|
||||
<div class="max-h-32 overflow-y-auto border rounded-md bg-background">
|
||||
<div data-tui-timepicker-minute-list="true" class="p-1 space-y-0.5">
|
||||
for minute := 0; minute < 60; minute += p.Step {
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-minute={ strconv.Itoa(minute) }
|
||||
data-tui-timepicker-selected="false"
|
||||
class="w-full px-2 py-1 text-sm rounded transition-colors text-left hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-selected=true]:bg-primary data-[tui-timepicker-selected=true]:text-primary-foreground data-[tui-timepicker-selected=true]:hover:bg-primary/90"
|
||||
>
|
||||
{ fmt.Sprintf("%02d", minute) }
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// AM/PM selector and action buttons
|
||||
<div class="flex justify-between items-center">
|
||||
if p.Use12Hours {
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-period="AM"
|
||||
data-tui-timepicker-active="false"
|
||||
class="px-3 py-1 text-sm rounded-md border transition-colors hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-active=true]:bg-primary data-[tui-timepicker-active=true]:text-primary-foreground data-[tui-timepicker-active=true]:hover:bg-primary/90"
|
||||
>
|
||||
{ p.AMLabel }
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-tui-timepicker-period="PM"
|
||||
data-tui-timepicker-active="false"
|
||||
class="px-3 py-1 text-sm rounded-md border transition-colors hover:bg-accent hover:text-accent-foreground data-[tui-timepicker-active=true]:bg-primary data-[tui-timepicker-active=true]:text-primary-foreground data-[tui-timepicker-active=true]:hover:bg-primary/90"
|
||||
>
|
||||
{ p.PMLabel }
|
||||
</button>
|
||||
</div>
|
||||
} else {
|
||||
<div></div>
|
||||
}
|
||||
@button.Button(button.Props{
|
||||
Type: "button",
|
||||
Variant: button.VariantSecondary,
|
||||
Size: button.SizeSm,
|
||||
Attributes: templ.Attributes{
|
||||
"data-tui-timepicker-done": "true",
|
||||
},
|
||||
}) {
|
||||
Done
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
templ Script() {
|
||||
<script defer nonce={ templ.GetNonce(ctx) } src={ "/assets/js/timepicker.min.js?v=" + utils.ScriptVersion }></script>
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue