chore: update templ and templui

This commit is contained in:
juancwu 2026-04-12 16:07:06 +00:00
commit 61eaa268ab
89 changed files with 25776 additions and 8231 deletions

View file

@ -1,4 +1,4 @@
// templui component timepicker - version: v1.2.0 installed by templui v1.2.0
// templui component timepicker - version: v1.9.5 installed by templui v1.9.5
// 📚 Documentation: https://templui.io/docs/components/time-picker
package timepicker
@ -56,7 +56,6 @@ templ TimePicker(props ...Props) {
p.Step = 1
}
var contentID = p.ID + "-content"
var valueString string
if p.Value != (time.Time{}) {
valueString = p.Value.Format("15:04")
@ -70,181 +69,185 @@ templ TimePicker(props ...Props) {
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 class="relative inline-block w-full" data-tui-timepicker-root>
@popover.Root() {
<input
type="hidden"
name={ p.Name }
value={ valueString }
if p.Form != "" {
form={ p.Form }
}
data-tui-timepicker-hidden-input="true"
/>
@popover.Trigger() {
@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"),
}),
}) {
<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++ {
<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{Class: "size-4"})
</span>
}
}
@popover.Content(popover.ContentProps{
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 }
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={ strconv.Itoa(hour) }
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"
>
{ fmt.Sprintf("%02d", hour) }
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>
}
}
} else {
// 24-hour format: 00-23
for hour := 0; hour < 24; hour++ {
</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-hour={ strconv.Itoa(hour) }
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", hour) }
{ fmt.Sprintf("%02d", minute) }
</button>
}
}
</div>
</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>
}
// 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>
</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>
// 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>
}
var scriptOnce = templ.NewOnceHandle()
templ Script() {
<script defer nonce={ templ.GetNonce(ctx) } src={ utils.ScriptURL("/assets/js/timepicker.min.js") }></script>
@scriptOnce.Once() {
@popover.Script()
@utils.ComponentScript("timepicker")
}
}