Skip to content

Commit debb7ad

Browse files
committed
feat(TimeInput): Allow null as value
1 parent 886db58 commit debb7ad

2 files changed

Lines changed: 15 additions & 16 deletions

File tree

docs/src/ui/time-input.stories.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ const ControlledStory = ({
3838
onChange,
3939
...props
4040
}: TimeInputProps) => {
41-
const [value, setValue] = useState(
42-
() => initialValue ?? new Temporal.PlainTime(13, 15)
43-
)
41+
const [value, setValue] = useState(() => initialValue ?? null)
4442
return (
4543
<TimeInput
4644
{...props}

src/components/ui/time-input.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import { clamp } from "../../utils/clamp"
1414
import { cn } from "../../utils/cn"
1515
import { ErrorBoundary } from "../utility/error-boundary"
1616

17-
const getNumbers = (value: Temporal.PlainTime | string = "") => {
17+
const getNumbers = (value?: Temporal.PlainTime | string | null) => {
1818
if (value instanceof Temporal.PlainTime) {
1919
const hours = value.hour.toString().padStart(2, "0")
2020
const minutes = value.minute.toString().padStart(2, "0")
2121
return `${hours}${minutes}`
2222
}
23-
return value.replaceAll(/[^\d]+/g, "").slice(0, 4)
23+
return (value ?? "").replaceAll(/[^\d]+/g, "").slice(0, 4)
2424
}
2525

2626
const parseNumbers = (value: string) => {
@@ -57,7 +57,8 @@ const forceTime = (value: string) => {
5757
return stringToTime(padded)
5858
}
5959

60-
const nextQuarter = (time: Temporal.PlainTime) => {
60+
const nextQuarter = (timeArg: Temporal.PlainTime | null = null) => {
61+
const time = timeArg ?? new Temporal.PlainTime()
6162
const toNextQuarter = 15 - (time.minute % 15)
6263
const duration = new Temporal.Duration(0, 0, 0, 0, 0, toNextQuarter)
6364
const newTime = time.add(duration)
@@ -67,7 +68,8 @@ const nextQuarter = (time: Temporal.PlainTime) => {
6768
return didOverflow ? max : newTime
6869
}
6970

70-
const prevQuarter = (time: Temporal.PlainTime) => {
71+
const prevQuarter = (timeArg: Temporal.PlainTime | null = null) => {
72+
const time = timeArg ?? new Temporal.PlainTime()
7173
const toPrevQuarter = time.minute % 15 || 15
7274
const duration = new Temporal.Duration(0, 0, 0, 0, 0, toPrevQuarter)
7375
const newTime = time.subtract(duration)
@@ -88,21 +90,19 @@ export interface TimeInputProps extends Pick<
8890
| "disabled"
8991
| "ref"
9092
> {
91-
value?: Temporal.PlainTime
93+
value?: Temporal.PlainTime | null
9294
onChange?: (
93-
value: Temporal.PlainTime,
95+
value: Temporal.PlainTime | null,
9496
event: ChangeEvent<HTMLInputElement> | KeyboardEvent<HTMLInputElement>
9597
) => void
9698
}
9799

98-
const defaultValue = new Temporal.PlainTime()
99-
100100
export const TimeInput = ({
101101
onKeyDown,
102102
onChange,
103103
onFocus,
104104
onBlur,
105-
value = defaultValue,
105+
value,
106106
className,
107107
...props
108108
}: TimeInputProps) => {
@@ -125,7 +125,7 @@ export const TimeInput = ({
125125
start === 5 ? value.slice(0, 3) + value.slice(4) : value.slice(0, 4)
126126

127127
setText(inserted)
128-
onChange?.(stringToTime(inserted), event)
128+
onChange?.(!inserted ? null : stringToTime(inserted), event)
129129
target.value = inserted
130130
target.setSelectionRange(start, start)
131131
}
@@ -165,12 +165,13 @@ export const TimeInput = ({
165165
aria-hidden
166166
className={cn(
167167
hstack({ align: "center", justify: "center" }),
168-
"pointer-events-none absolute inset-0 m-auto size-full text-sm [&:has(+input:focus)]:opacity-0"
168+
"pointer-events-none absolute inset-0 m-auto size-full text-sm [&:has(+input:focus)]:opacity-0",
169+
!value && "text-text-gentle"
169170
)}
170171
>
171-
{text.slice(0, 2).padEnd(2, "-")}
172+
{text.slice(0, 2) || "hh"}
172173
<span className="mx-0.5 font-bold opacity-50">:</span>
173-
{text.slice(2, 4).padEnd(2, "-")}
174+
{text.slice(2, 4) || "mm"}
174175
</div>
175176

176177
<input

0 commit comments

Comments
 (0)