# 时间输入框

## 使用方法

`TimeInput` 编辑一天里的时间。值是 ISO 时间，`HH:MM` 或 `HH:MM:SS`，没有选择时是 `null`。

```vue
<script setup lang="ts">
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const time = shallowRef<string | null>('14:30')
</script>

<template>
  <TimeInput v-model="time" label="时间" />
</template>
```

默认按 12 小时显示。方向键按 `increment` 分钟步进。细指针下是文本框。`adaptive-native` 在粗指针上用系统时间控件；`adaptive-bottom-sheet` 在粗指针上用底部时分滚轮；`bottom-sheet` 不论指针都用滚轮。需要秒、步进不是 1 分钟，或设了预设间隔时，系统控件退回文本框，滚轮不受这个限制。占位符和清除文案跟随语言。

### 结构

- **时钟图标**: 前导时钟图标。它标识这个字段，并在原生模式打开浏览器或系统选择器。
- **时间控件**: 原生模式是真正的 input type=time。精细指针、presentation 为 text-input、需要秒或自定义步进时，是 Astryx 可编辑的文本字段。
- **清除按钮**: 尾部用来重置值的按钮。hasClear 为真且已经有值时显示。
- **状态图标**: 尾部图标，表示错误、警告或成功。
- **转圈**: 加载时替换尾部内容，表示异步操作还在进行。

## 最佳实践

| 指引 | 实践 |
| --- | --- |
| 推荐 | 只有一段时间合法时，设置 `min` 和 `max`。 |
| 推荐 | 产品本身用 24 小时制时，设置 `hourFormat="24h"`。 |
| 避免 | 在这里收集日期。日期用 DateInput，日期加时间用 DateTimeInput。 |

## 示例

### 限制范围

晚餐只接受下午 5 点到晚上 10 点。

```vue
<script setup lang="ts">
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const evening = shallowRef<string | null>(null)
</script>

<template>
  <TimeInput
    v-model="evening"
    label="Dinner reservation"
    min="17:00"
    max="22:00"
    description="Evening seating: 5 PM – 10 PM"
    placeholder="Select reservation time"
    presentation="adaptive-bottom-sheet"
    has-clear
  />
</template>
```
### 格式

24 小时制，以及保留秒的值。

```vue
<script setup lang="ts">
import { VStack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const time24 = shallowRef('14:30')
const timeSec = shallowRef('14:30:45')
</script>

<template>
  <VStack :gap="4">
    <Text type="supporting" color="secondary">
      Format variations for different contexts
    </Text>
    <VStack :gap="3">
      <TimeInput v-model="time24" label="24-hour" hour-format="24h" />
      <TimeInput v-model="timeSec" label="With seconds" has-seconds />
    </VStack>
  </VStack>
</template>
```
### 步进

方向键每次移动 15 分钟。

```vue
<script setup lang="ts">
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const slot = shallowRef('09:00')
</script>

<template>
  <TimeInput
    v-model="slot"
    label="Appointment slot"
    :increment="15"
    description="Use arrow keys to change by 15 minutes"
    has-clear
  />
</template>
```
### 状态

禁用、错误、警告和成功。

```vue
<script setup lang="ts">
import { VStack } from '@astryx-vue/core/Stack'
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const disabledVal = shallowRef('10:00')
const errorVal = shallowRef('22:00')
const warningVal = shallowRef('07:00')
const successVal = shallowRef('10:00')
</script>

<template>
  <VStack :gap="3">
    <TimeInput v-model="disabledVal" label="Disabled field" is-disabled />
    <TimeInput v-model="errorVal" label="Error message" :status="{ type: 'error', message: 'Time must be during business hours' }" />
    <TimeInput v-model="warningVal" label="Warning message" :status="{ type: 'warning', message: 'Early morning — are you sure?' }" />
    <TimeInput v-model="successVal" label="Success message" :status="{ type: 'success', message: 'Time slot is available' }" />
  </VStack>
</template>
```
### 底部面板

不论指针类型，都用底部的小时和分钟滚轮。

```vue
<script setup lang="ts">
import { TimeInput } from '@astryx-vue/core/TimeInput'
import { shallowRef } from 'vue'

const time = shallowRef<string | null>('14:30')
</script>

<template>
  <div style="width: 240px; max-width: 100%">
    <TimeInput
      v-model="time"
      label="Start"
      presentation="bottom-sheet"
    />
  </div>
</template>
```

## TimeInput 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `v-model` | `string \| null` | `null` | ISO 时间，`HH:MM` 或 `HH:MM:SS`。 |
| `label` | `string` | — | 必填。无障碍名称。 |
| `hourFormat` | `'12h' \| '24h'` | `'12h'` | 显示格式。模型值始终是 24 小时 ISO。 |
| `hasSeconds` | `boolean` | `false` | 值里保留秒。 |
| `increment` | `number` | `1` | 方向键每次增减的分钟数。 |
| `min` | `string` | — | 最早的 ISO 时间。 |
| `max` | `string` | — | 最晚的 ISO 时间。 |
| `presentation` | `'text-input' \| 'native' \| 'adaptive-native' \| 'adaptive-bottom-sheet' \| 'popover' \| 'bottom-sheet'` | `'adaptive-native'` | 文本框、系统时间控件，或底部时分滚轮。不传时细指针用文本框，粗指针用系统控件。adaptive-bottom-sheet 只在粗指针上用滚轮；bottom-sheet 始终用滚轮。有秒、步进不是 1，或设了预设间隔时，系统控件退回文本框，滚轮不受影响。显式值压过 nativePicker。 |
| `isLabelHidden` | `boolean` | `false` | 视觉上隐藏标签，读屏仍然读得到。旧名 `labelHidden` 同样可用，两者任一为真就隐藏。 |
| `containerClass` | `string \| object \| array` | — | 加到字段外壳的 class。在 InputGroup 和横向标签的 FormLayout 里没有外壳，此时不生效，开发环境会警告。 |
| `containerStyle` | `StyleValue` | — | 加到字段外壳的行内样式。落点规则同 containerClass。 |
| `controlClass` | `string \| object \| array` | — | 加到带边框本体的 class。直接写在组件上的 class 也落在本体，不会落到内部输入框上。 |
| `controlStyle` | `StyleValue` | — | 加到带边框本体的行内样式。 |
| `hasClear` | `boolean` | `false` | 显示清除按钮。 |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 字段高度。 |
| `status` | `FieldStatusInput` | — | 错误、警告或成功。 |
| `statusVariant` | `'attached' \| 'detached' \| 'tooltip'` | `'attached'` | 状态消息的位置。 |
| `isDisabled` | `boolean` | `false` | 禁用字段。 |
| `disabledMessage` | `string` | — | 禁用原因。和 `isDisabled` 一起用时，悬停或键盘聚焦会显示这段提示，输入框仍可聚焦（带 `aria-disabled`），但改不了值。 |
| `isLoading` | `boolean` | `false` | 字段不可改，并显示加载指示。 |
| `loadingLabel` | `string` | `加载中` | 加载指示的读屏文本。 |
| `labelTooltip` | `string` | — | 标签末尾信息图标里的说明。 |
| `placeholder` | `string` | `选择时间` | 空值文案。聚焦且为空时，改成格式提示。 |
