@xsolla/xui-input-time
v0.184.0
Published
A structured time input component for entering hours, minutes, and optionally seconds. Supports 12h and 24h formats with an optional AM/PM toggle. Cross-platform (web and React Native). <!-- BEGIN:xui-mcp-instructions:input-time --> A segmented time input
Readme
Input Time
A structured time input component for entering hours, minutes, and optionally seconds. Supports 12h and 24h formats with an optional AM/PM toggle. Cross-platform (web and React Native).
A segmented time input that renders individual editable cells for hours (HH), minutes (MM), and optionally seconds (SS), plus an optional AM/PM toggle. Each segment is an independent .input-time item that can be focused and edited separately. Supports five sizes, an optional leading icon, and the full standard input state set.
When to use
When the user must specify an exact time — scheduling a task, setting a notification, entering a meeting time, configuring a cron-like trigger
When time entry precision matters and a free-text field would allow invalid formats
- As part of a DatePicker input alongside a Calendar
- When the user should be able to edit individual time units (hours only, or minutes without touching hours) without retyping the whole value
When not to use
When only approximate time ranges are needed — use a Select with options like "Morning", "Afternoon", "Evening"
When the user selects from a fixed set of times — use a Select or ContextMenu with time options
When the time input is a single free-text field is sufficient and validation is handled elsewhere — use a standard Input with type="time"
Content guidelines
Placeholder text — use HH, MM, SS as placeholder labels inside the respective segment cells. For 12-hour format, the HH segment shows 12 as a default when unfilled.
Field label — always provide a visible label above the component: "Start time", "Event time", "Schedule at". Do not rely on the clock icon alone. Error messages — be specific:
- "Please enter a valid time"
- "Time must be between 09:00 and 18:00"
- "End time must be after start time"
- AM/PM toggle labels — use exactly AM and PM. Do not use a.m./p.m. or localised equivalents inside the toggle; keep the toggle label short.
Behaviour guidelines
Segment focus and navigation — clicking a segment cell focuses it and highlights the current value for replacement. After the user types a valid two-digit value in a cell, focus automatically advances to the next segment (HH → MM → SS → AM/PM). Tab also advances focus in the same order.
Digit-by-digit entry — each segment accepts one or two digit keystrokes. The first keystroke shows the first digit with a cursor; the second completes the segment and auto-advances. If the first digit would make a two-digit number impossible (e.g. typing 3 in an HH field in 24-hour mode would mean only 30–39 are valid, but hours go to 23), auto-advance immediately with a leading zero where needed.
Arrow key increment — when a segment is focused, ↑ increments the value by 1 and ↓ decrements it, cycling within the valid range (e.g. 23 → 00 for hours, 59 → 00 for minutes/seconds). This allows fine-tuning without retyping.
Invalid values — out-of-range values (e.g. 25 for hours, 61 for minutes) are rejected on blur. Clamp the value to the maximum valid value rather than clearing the field. Show State=Error at the container level if the time is structurally invalid.
Backspace — pressing Backspace in a focused segment clears its value (sets Fill=False) and returns focus to the previous segment if the current segment was already empty.
AM/PM toggle — clicking the AM/PM toggle switches between AM and PM. Pressing A or P while the toggle is focused also switches directly to the corresponding mode. The hour value displayed in the HH segment does not change when toggling AM/PM.
Paste — support pasting a time string (e.g. 14:30, 2:30 PM, 09:45:00) into the component. Parse the pasted value and distribute digits to the appropriate segments.
Validation — validate on blur of the entire component (when focus leaves all segments). Do not show an error while the user is mid-entry.
Disabled state — all segments and the AM/PM toggle must be non-interactive. If a time value is present, show it in muted style. Provide a tooltip or nearby label explaining why the field is disabled if the reason is not obvious.
Accessibility
Each segment cell must be implemented as a focusable element — either an <input type="number"> or a custom element with role="spinbutton", aria-valuemin, aria-valuemax, aria-valuenow, and aria-label identifying the segment (e.g. aria-label="Hours", aria-label="Minutes", aria-label="Seconds").
The container must have a role="group" with aria-label describing the whole field: e.g. aria-label="Event start time".
Arrow key navigation (↑/↓) on a focused role="spinbutton" segment must increment/decrement the value and announce the new value via the aria-valuenow attribute update.
The AM/PM toggle must be a or role="switch" with aria-label="AM" / aria-label="PM" reflecting the current state, and aria-pressed or aria-checked set accordingly.
When State=Error, the error message must be linked via aria-describedby on the container group so it is announced when any segment receives focus.
When State=Disable, all interactive elements must have aria-disabled="true".
Auto-advance (moving focus to the next segment) must also move the DOM focus, not just visual state. Announce the segment name when focus advances.
Installation
npm install @xsolla/xui-input-timeDemo
Basic Usage
import * as React from "react";
import { InputTime } from "@xsolla/xui-input-time";
import type { TimeValue } from "@xsolla/xui-input-time";
export default function BasicTime() {
const [value, setValue] = React.useState<TimeValue | null>(null);
return <InputTime value={value} onChange={setValue} />;
}With Seconds
import * as React from "react";
import { InputTime } from "@xsolla/xui-input-time";
import type { TimeValue } from "@xsolla/xui-input-time";
export default function WithSeconds() {
const [value, setValue] = React.useState<TimeValue | null>(null);
return <InputTime value={value} onChange={setValue} showSeconds />;
}12-Hour Format with AM/PM
import * as React from "react";
import { InputTime } from "@xsolla/xui-input-time";
import type { TimeValue } from "@xsolla/xui-input-time";
export default function TwelveHour() {
const [value, setValue] = React.useState<TimeValue | null>({
hours: 3,
minutes: 30,
period: "pm",
});
return (
<InputTime value={value} onChange={setValue} hourCycle={12} showPeriod />
);
}Custom Icon
A Clock icon is displayed by default. Pass a custom icon via the icon prop, or set icon={null} to hide it.
import * as React from "react";
import { InputTime } from "@xsolla/xui-input-time";
import { Clock } from "@xsolla/xui-icons-base";
import type { TimeValue } from "@xsolla/xui-input-time";
export default function CustomIcon() {
const [value, setValue] = React.useState<TimeValue | null>(null);
return (
<InputTime
value={value}
onChange={setValue}
icon={<Clock variant="solid" />}
/>
);
}Different Sizes
import * as React from "react";
import { InputTime } from "@xsolla/xui-input-time";
export default function Sizes() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
<InputTime size="xl" />
<InputTime size="lg" />
<InputTime size="md" />
<InputTime size="sm" />
<InputTime size="xs" />
</div>
);
}API Reference
InputTime
InputTimeProps:
| Prop | Type | Default | Description |
| :---------- | :------------------------------------- | :------------- | :------------------------------------------------------------------------------------------------------------ |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
| value | TimeValue \| null | - | Current time value. |
| onChange | (value: TimeValue \| null) => void | - | Time change callback. |
| showSeconds | boolean | false | Show seconds segment (HH:MM:SS). |
| showPeriod | boolean | false | Show AM/PM toggle. |
| hourCycle | 12 \| 24 | 24 | Hour format (12h or 24h). |
| icon | ReactNode | <Clock /> | Icon displayed on the left. Set to null to hide. |
| size | "xl" \| "lg" \| "md" \| "sm" \| "xs" | "md" | Input size variant. |
| disabled | boolean | false | Disable the input. |
| error | string | - | Error message (shows error state). |
| aria-label | string | "Time input" | Accessible label. |
| testID | string | - | Test identifier. |
TimeValue
| Property | Type | Description |
| :------- | :------------------------ | :-------------------------------------------------- |
| hours | number | Hours (0-23 for 24h, 1-12 for 12h). |
| minutes | number | Minutes (0-59). |
| seconds | number (optional) | Seconds (0-59). Present when showSeconds is true. |
| period | "am" \| "pm" (optional) | Time period. Present when showPeriod is true. |
Keyboard Navigation
| Key | Action | | :--------- | :-------------------------------------------------------- | | 0-9 | Type digit. Auto-advances to next segment after 2 digits. | | Tab | Move to next segment. | | Shift+Tab | Move to previous segment. | | ArrowUp | Increment focused segment by 1. | | ArrowDown | Decrement focused segment by 1. | | ArrowRight | Move to next segment. | | ArrowLeft | Move to previous segment. | | Backspace | Clear segment and move back. |
Validation
Values are clamped to valid ranges automatically:
- Hours: 0-23 (24h) or 1-12 (12h)
- Minutes: 0-59
- Seconds: 0-59
Accessibility
- Each segment has an
aria-label("Hours", "Minutes", "Seconds") - Error messages use
role="alert" - AM/PM toggle is a button with descriptive
aria-label - Segments are grouped with
role="group"
