# 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).
<!-- BEGIN:xui-mcp-instructions:input-time -->
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 <button> 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.
<!-- END:xui-mcp-instructions:input-time -->

## Installation

```bash
npm install @xsolla/xui-input-time
```

## Demo

### Basic Usage

```tsx
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

```tsx
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

```tsx
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.

```tsx
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

```tsx
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"`
