Exibe loading em máquinas e moderniza time picker

This commit is contained in:
codex-bot 2025-10-23 15:06:41 -03:00
parent 9bfdb451bc
commit aef5e66718
2 changed files with 57 additions and 67 deletions

View file

@ -1,10 +1,9 @@
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Clock8Icon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Input } from "@/components/ui/input"
import { cn } from "@/lib/utils"
type TimePickerProps = {
@ -13,71 +12,39 @@ type TimePickerProps = {
className?: string
placeholder?: string
stepMinutes?: number
disabled?: boolean
}
function pad2(n: number) {
return String(n).padStart(2, "0")
}
export function TimePicker({ value, onChange, className, placeholder = "Selecionar horário", stepMinutes = 15, disabled }: TimePickerProps) {
const handleChange = React.useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(event.target.value)
},
[onChange]
)
export function TimePicker({ value, onChange, className, placeholder = "Selecionar horário", stepMinutes = 15 }: TimePickerProps) {
const [open, setOpen] = React.useState(false)
const [hours, minutes] = React.useMemo(() => {
if (!value || !/^\d{2}:\d{2}$/.test(value)) return ["", ""]
const [h, m] = value.split(":")
return [h, m]
}, [value])
const minuteOptions = React.useMemo(() => {
const list: string[] = []
for (let i = 0; i < 60; i += stepMinutes) list.push(pad2(i))
if (!list.includes("00")) list.unshift("00")
return list
const stepSeconds = React.useMemo(() => {
if (!stepMinutes || stepMinutes <= 0) return undefined
return Math.max(1, Math.round(stepMinutes * 60))
}, [stepMinutes])
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="outline" className={cn("w-full justify-between font-normal", className)}>
{value ? value : placeholder}
<ChevronDownIcon className="ml-2 size-4 opacity-60" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="flex gap-2 p-2">
<div className="max-h-56 w-16 overflow-auto rounded-md border">
{Array.from({ length: 24 }, (_, h) => pad2(h)).map((h) => (
<button
key={h}
type="button"
className={cn(
"block w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
h === hours && "bg-muted/70 font-semibold"
)}
onClick={() => onChange?.(`${h}:${minutes || "00"}`)}
>
{h}
</button>
))}
</div>
<div className="max-h-56 w-16 overflow-auto rounded-md border">
{minuteOptions.map((m) => (
<button
key={m}
type="button"
className={cn(
"block w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
m === minutes && "bg-muted/70 font-semibold"
)}
onClick={() => onChange?.(`${hours || "00"}:${m}`)}
>
{m}
</button>
))}
</div>
</div>
</PopoverContent>
</Popover>
<div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-muted-foreground">
<Clock8Icon aria-hidden className="size-4" />
</div>
<Input
type="time"
value={value ?? ""}
onChange={handleChange}
step={stepSeconds}
disabled={disabled}
aria-label={placeholder}
className={cn(
"pl-9 text-sm [appearance:textfield] [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none",
className
)}
/>
</div>
)
}