# Time Picker
> Time Picker component enables users to enter a time through text input or list of options from the dropdown menu.

## Import

```js
import { TimePicker } from '@gemini-suite/vera-react';
```

## Examples

### Basic

`TimePicker` is modelled around the [Combobox pattern](/components/forms/combobox), so a user can enter a time either through text input or options menu with a list of predefined values.

```jsx
<TimePicker aria-label="Time picker" />
```

You can pass `defaultValue` for uncontrolled `TimePicker`.

```jsx
<TimePicker
  defaultValue="11:00"
  onValueChange={time => {
    console.log(`Time changed to ${time}`);
  }}
  aria-label="Time picker with default value"
/>
```

### Controlled

You can easily make the `TimePicker` controlled, by passing your own state to `value` prop. `onValueChange` handler is called when
the time value changes, allowing you to sync state.

```jsx
() => {
  const [value, setValue] = React.useState('10:00');

  return (
    <Flex flow="column">
      <TimePicker
        value={value}
        onValueChange={setValue}
        aria-label="Controlled time picker"
      />
      <Box>{value ? `Selected time: ${value}` : null}</Box>
    </Flex>
  );
};
```

### With Form Field

Use `TimePicker` with [Form Field](/components/forms/form-field) in order to enhance layout with a clear label. Optionally, you may want to connect helper text and/or validation feedback message to the `TimePicker`.

<Callout variant="tip">

When using `TimePicker` without `FormField.Label`, be mindful to provide `aria-label` instead.

</Callout>

```jsx
() => {
  const [value, setValue] = React.useState('');

  return (
    <FormField.Root
      as={Flex}
      flow="column"
      isRequired
      validationStatus={!value ? 'error' : undefined}
    >
      <FormField.Label>{'Lunchtime'}</FormField.Label>
      <TimePicker value={value} onValueChange={setValue} />
      <FormField.FeedbackMessage>
        <FormField.FeedbackIcon />
        {'Feedback message.'}
      </FormField.FeedbackMessage>
    </FormField.Root>
  );
};
```

### Validation

Since `TimePicker` allows user to enter a time through a text input, it has to handle invalid user input, i.e. not meeting the expected time format.

By default, `TimePicker` attempts to parse the provided input into a `HH:MM` format, as shown in the placeholder.
If that fails, the text field will highlight itself in red to provide visual cue that it needs correction.
You can subscribe to the `onError` callback which is triggered when the input error changes and use the
`FormField.FeedbackMessage` to communicate a detailed error message to the user.

<Callout variant="tip">

`onError` callback provides a reason for the error which can be either an incorrect format.

</Callout>

```jsx
() => {
  const [error, setError] = React.useState(null);

  return (
    <FormField.Root
      as={Flex}
      flow="column"
      validationStatus={error ? 'error' : undefined}
    >
      <FormField.Label>{'Delivery time'}</FormField.Label>
      <FormField.HelperText>
        {'Type in invalid time to see the error message.'}
      </FormField.HelperText>
      <TimePicker onError={newError => setError(newError)} />
      <FormField.FeedbackMessage>
        <FormField.FeedbackIcon />
        {error === 'invalidTime' ? 'Please provide a valid time.' : ''}
      </FormField.FeedbackMessage>
    </FormField.Root>
  );
};
```

### Disabled state

Pass `isDisabled` prop to the accompanying `FormField` and it will cascade down to the `Timepicker`.

```jsx
<FormField.Root as={Flex} flow="column" isDisabled>
  <FormField.Label>{'Start time'}</FormField.Label>
  <TimePicker />
</FormField.Root>
```

You can also pass `isDisabled` prop directly to `DatePicker`.

```jsx
<TimePicker isDisabled aria-label="Start time" />
```

### Sizes

As with other types of input controls, `TimePicker` comes in two sizes: `medium` and `small`. By default, it uses `medium` size.

```jsx
<Flex flow="column">
  <FormField.Root as={Flex} flow="column" size="small">
    <FormField.Label>{'Small'}</FormField.Label>
    <TimePicker size="small" />
  </FormField.Root>
  <FormField.Root as={Flex} flow="column">
    <FormField.Label>{'Medium'}</FormField.Label>
    <TimePicker />
  </FormField.Root>
</Flex>
```

### Step

By default, values in the options menu are created from 24 hours timespan with "steps" of 15 minute intervals.
The list of values can be modified by changing the `step` parameter.

<Callout variant="important">

Note that the maximum value of `step` can be `60` minutes.

</Callout>

```jsx
<FormField.Root as={Flex} flow="column">
  <FormField.Label>{'Lunch hour'}</FormField.Label>
  <TimePicker step={60} />
</FormField.Root>
```

### No custom value

By default, `TimePicker` accepts values that are not listed in the options list, assuming the format is correct.
To prevent that and limit allowed values only to the pre-determined list of options, set `allowsCustomValue` prop to `false`.

```jsx
() => {
  const [value, setValue] = React.useState('');

  return (
    <Flex flow="column">
      <FormField.Root as={Flex} flow="column">
        <FormField.Label>{'Lunch hour'}</FormField.Label>
        <TimePicker
          allowsCustomValue={false}
          value={value}
          onValueChange={setValue}
          step={60}
        />
      </FormField.Root>
      <Box>{value ? `Selected time: ${value}` : null}</Box>
    </Flex>
  );
};
```

### Date and time picker

You can combine `TimePicker` with [Date Picker component](/components/forms/date-picker]) to allow user to select both date and time.

Use layout components like [Flex](/components/layout/flex) or [Grid](/components/layout/grid) to control arrangement of the fields.

<Callout variant="tip">

In the example below `useDateFormatter` hook exported by Vera is used to display selected date and time.
It returns memoized instance of [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) under the hood.

If you're already using a library such as [date-fns](https://date-fns.org/), you could alternatively use it's `format` utility.

</Callout>

```jsx
() => {
  const [dateValidationError, setDateValidationError] = React.useState(null);
  const [timeValidationError, setTimeValidationError] = React.useState(null);

  const [time, setTime] = React.useState('');
  const [date, setDate] = React.useState(null);

  const [dateWithTime, setDateWithTime] = React.useState(null);

  const handleDateChange = newDate => {
    let newDateWithTime;

    if (newDate && time) {
      const [hours, minutes] = time.split(':');
      newDateWithTime = new Date(newDate.getTime());
      newDateWithTime.setHours(parseInt(hours, 10), parseInt(minutes, 10));
    }

    setDateWithTime(newDateWithTime ? newDateWithTime : newDate);
    setDate(newDate);
  };

  const handleTimeChange = newTime => {
    if (date) {
      const newDateWithTime = new Date(date.getTime());

      if (newTime) {
        const [hours, minutes] = newTime.split(':');
        newDateWithTime.setHours(parseInt(hours, 10), parseInt(minutes, 10));
      } else {
        newDateWithTime.setHours(0, 0);
      }

      setDateWithTime(newDateWithTime);
    }

    setTime(newTime);
  };

  const dateFormatter = useDateFormatter(undefined, {
    hour12: false,
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit'
  });

  return (
    <ContentBlock max="measureMedium" center={false} gutters="none">
      <Flex flow="column">
        <Grid
          columns={{
            '@initial': '1',
            '@mqMediumAndUp': '2'
          }}
        >
          <GridItem>
            <FormField.Root
              as={Flex}
              flow="column"
              validationStatus={dateValidationError ? 'error' : undefined}
            >
              <FormField.Label>{'Date'}</FormField.Label>
              <DatePicker.Root
                value={date}
                onValueChange={handleDateChange}
                onError={newError => setDateValidationError(newError)}
              >
                <DatePicker.TextInput
                  trailingVisual={
                    <DatePicker.CalendarTrigger aria-label="Use calendar" />
                  }
                />
                <DatePicker.Calendar />
              </DatePicker.Root>
              <FormField.FeedbackMessage>
                <FormField.FeedbackIcon />
                {dateValidationError === 'invalidDate'
                  ? 'Please provide a valid date.'
                  : ''}
              </FormField.FeedbackMessage>
            </FormField.Root>
          </GridItem>
          <GridItem>
            <FormField.Root
              as={Flex}
              flow="column"
              validationStatus={timeValidationError ? 'error' : undefined}
            >
              <FormField.Label>{'Time'}</FormField.Label>
              <TimePicker
                value={time}
                onValueChange={handleTimeChange}
                onError={newError => setTimeValidationError(newError)}
              />
              <FormField.FeedbackMessage>
                <FormField.FeedbackIcon />
                {timeValidationError === 'invalidTime'
                  ? 'Please provide a valid time.'
                  : ''}
              </FormField.FeedbackMessage>
            </FormField.Root>
          </GridItem>
        </Grid>
        {dateWithTime ? (
          <Box>{`Selected date and time: ${dateFormatter.format(
            dateWithTime
          )}`}</Box>
        ) : null}
      </Flex>
    </ContentBlock>
  );
};
```

---

## Accessibility features

<ul>
  <li iconName="check">

`Timepicker` provides deep keyboard interactions.

  </li>
  <li iconName="check">

Necessary ARIA roles and attributes are provided to give directions to a screen reader user.

  </li>
</ul>

---

## API Reference

### TimePicker

| Name                | Type                                                          | Default    | Description                                                                                                                                                                                                                                    | Required |
| ------------------- | ------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `defaultValue`      | `string`                                                      |            | The initial value the time picker, when component is first rendered. Use when you do not need to control the state of the picker.                                                                                                              |          |
| `value`             | `string`                                                      |            | The controlled value of the picker. Use in conjunction with `onValueChange`.                                                                                                                                                                   |          |
| `onValueChange`     | `(time: string) => void`                                      |            | Event handler called when the value changes.                                                                                                                                                                                                   |          |
| `size`              | `"medium" \| "small"`                                         | `"medium"` | The size of the time picker.                                                                                                                                                                                                                   |          |
| `step`              | `number`                                                      |            | Time interval (in minutes) between values in time picker options list. Maximum value is 60 minutes.                                                                                                                                            |          |
| `locale`            | `string \| Locale`                                            |            | A string that represents [locale code](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument) or [date-fns locale object](https://date-fns.org/docs/Locale) used to localize the time format. |          |
| `isDisabled`        | `boolean`                                                     |            | When `true`, the time picker will be disabled.                                                                                                                                                                                                 |          |
| `isRequired`        | `boolean`                                                     |            | When `true`, the time picker is marked as required.                                                                                                                                                                                            |          |
| `allowsCustomValue` | `boolean`                                                     | `true`     | When `true`, text input of time picker allows to set a value that is not listed in the pre-determined options.                                                                                                                                 |          |
| `showIcon`          | `boolean`                                                     | `true`     | When `true`, a time icon will show on the right edge of the input.                                                                                                                                                                             |          |
| `onError`           | `(error: TimeValidationError \| null, value: string) => void` |            | Event handler called when the error associated to the user-typed value changes.                                                                                                                                                                |          |
