# 时间编辑器组件

一个专为时间输入设计的组件，具备交互式时间选择功能。支持键盘导航、自动验证，以及分钟和秒精度。时间以表示总分钟数的数字值存储（支持秒的 fractional 表示）。

## 安装

```bash
npm install @ticatec/uniface-element
```

## 导入

```typescript
import TimeEditor from "@ticatec/uniface-element/TimeEditor";
```

## 基本用法

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let timeValue = null; // null 或数字（分钟数）
</script>

<TimeEditor bind:value={timeValue} />
```

## 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `value` | `number \| null` | `null` | 时间值（分钟数，例如 90.5 = 1:30:30） |
| `precision` | `"M" \| "S"` | `"M"` | 时间精度 - 分钟或秒 |
| `mandatory` | `boolean` | `false` | 是否为必填字段（空字段显示零而不是下划线） |
| `disabled` | `boolean` | `false` | 输入框是否禁用 |
| `readonly` | `boolean` | `false` | 输入框是否只读 |
| `variant` | `"" \| "plain" \| "outlined" \| "filled"` | `""` | 输入框的视觉变体 |
| `compact` | `boolean` | `false` | 是否使用紧凑间距 |
| `style` | `string` | `""` | 附加 CSS 样式 |
| `prefix` | `string` | `""` | 显示的文本前缀 |
| `suffix` | `string` | `""` | 显示的文本后缀 |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式（编辑/查看） |
| `class` | `string` | `""` | CSS 类名 |
| `onchange` | `OnChangeHandler<number>` | `null` | 时间值更改时的回调函数 |
| `onfocus` | `((event: FocusEvent) => void) \| null` | `null` | 输入框获得焦点时的回调函数 |
| `onblur` | `((event: FocusEvent) => void) \| null` | `null` | 输入框失去焦点时的回调函数 |

## 功能

- **交互式选择**：点击或使用箭头键在时间段之间导航
- **自动验证**：防止无效时间输入（例如 25:70）
- **灵活精度**：支持分钟（HH:MM）和秒（HH:MM:SS）精度
- **清除功能**：内置清除按钮以重置时间
- **视觉反馈**：高亮显示当前光标所在的时间段
- **必填模式**：可选模式显示零而不是下划线用于必填字段

## 值格式

时间编辑器以表示总分钟数的数字存储时间：
- `90` = 1小时30分钟（01:30）
- `90.5` = 1小时30分钟30秒（01:30:30）
- `null` = 空/未设置时间

## 示例

### 基本时间输入（分钟）

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let meetingTime = 570; // 9:30 AM (9*60 + 30 = 570 分钟)
</script>

<div class="form-group">
  <label>会议时间：</label>
  <TimeEditor bind:value={meetingTime} precision="M" />
</div>

<p>选择的时间：{meetingTime ? Math.floor(meetingTime / 60) + ':' + (meetingTime % 60).toString().padStart(2, '0') : '未设置'}</p>

<style>
  .form-group {
    margin-bottom: 16px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
</style>
```

### 带秒的时间输入

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let duration = 125.75; // 2:05:45 (125 分钟 + 45 秒)
</script>

<div class="form-group">
  <label>视频时长：</label>
  <TimeEditor bind:value={duration} precision="S" />
</div>
```

### 带事件处理程序

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let scheduleTime = null;
  let isFocused = false;
  
  function handleTimeChange(newTime) {
    console.log('时间更改：', newTime);
    if (newTime !== null) {
      const hours = Math.floor(newTime / 60);
      const minutes = Math.floor(newTime % 60);
      const seconds = Math.floor((newTime % 1) * 60);
      console.log(`时间：${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`);
    }
  }
  
  function handleFocus(event) {
    console.log('时间编辑器已聚焦');
    isFocused = true;
  }
  
  function handleBlur(event) {
    console.log('时间编辑器已失焦');
    isFocused = false;
  }
</script>

<TimeEditor 
  bind:value={scheduleTime}
  precision="S"
  onchange={handleTimeChange}
  onfocus={handleFocus}
  onblur={handleBlur}
/>

{#if isFocused}
  <p>时间编辑器处于活动状态 - 使用箭头键导航，数字键输入</p>
{/if}
```

### 必填模式

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let time1 = null;
  let time2 = null;
</script>

<!-- 可选模式（显示下划线） -->
<div class="form-group">
  <label>可选时间：</label>
  <TimeEditor bind:value={time1} mandatory={false} />
</div>

<!-- 必填模式（显示零） -->
<div class="form-group">
  <label>必填时间：</label>
  <TimeEditor bind:value={time2} mandatory={true} />
</div>
```

### 不同变体

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let time1 = 480; // 8:00
  let time2 = 540; // 9:00
  let time3 = 600; // 10:00
</script>

<!-- 默认变体 -->
<TimeEditor bind:value={time1} />

<!-- 轮廓变体 -->
<TimeEditor bind:value={time2} variant="outlined" />

<!-- 填充变体 -->
<TimeEditor bind:value={time3} variant="filled" />
```

### 带前缀和后缀

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let startTime = 480; // 8:00 AM
  let duration = 120;  // 2:00 小时
</script>

<div class="form-group">
  <label>开始时间：</label>
  <TimeEditor bind:value={startTime} suffix=" 上午" />
</div>

<div class="form-group">
  <label>持续时间：</label>
  <TimeEditor bind:value={duration} prefix="持续时间：" suffix=" 小时" />
</div>
```

### 日程表单示例

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  import Button from "@ticatec/uniface-element/Button";
  import TextEditor from "@ticatec/uniface-element/TextEditor";
  
  let schedule = {
    title: '',
    startTime: null,
    endTime: null,
    breakDuration: 15 // 15 分钟
  };
  
  $: totalDuration = schedule.startTime !== null && schedule.endTime !== null 
    ? schedule.endTime - schedule.startTime - schedule.breakDuration
    : 0;
  
  function formatTime(minutes) {
    if (minutes === null) return '未设置';
    const hours = Math.floor(minutes / 60);
    const mins = Math.floor(minutes % 60);
    return `${hours}:${mins.toString().padStart(2, '0')}`;
  }
  
  function handleSubmit() {
    console.log('日程：', schedule);
  }
</script>

<div class="schedule-form">
  <h3>创建日程</h3>
  
  <div class="form-field">
    <label>事件标题</label>
    <TextEditor bind:value={schedule.title} placeholder="输入事件标题" />
  </div>
  
  <div class="form-row">
    <div class="form-field">
      <label>开始时间</label>
      <TimeEditor bind:value={schedule.startTime} precision="M" />
    </div>
    
    <div class="form-field">
      <label>结束时间</label>
      <TimeEditor bind:value={schedule.endTime} precision="M" />
    </div>
  </div>
  
  <div class="form-field">
    <label>休息时间（分钟）</label>
    <TimeEditor bind:value={schedule.breakDuration} precision="M" />
  </div>
  
  {#if totalDuration > 0}
    <div class="summary">
      <p><strong>概要：</strong></p>
      <p>持续时间：{formatTime(totalDuration)}（不包括休息时间）</p>
      <p>开始：{formatTime(schedule.startTime)} - 结束：{formatTime(schedule.endTime)}</p>
    </div>
  {/if}
  
  <Button 
    type="primary" 
    label="创建日程" 
    onClick={handleSubmit}
    disabled={!schedule.title || schedule.startTime === null || schedule.endTime === null}
  />
</div>

<style>
  .schedule-form {
    max-width: 500px;
    padding: 24px;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  h3 {
    margin-bottom: 24px;
  }
  
  .form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }
  
  .form-field {
    margin-bottom: 16px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
  
  .summary {
    background: #f8f9fa;
    padding: 16px;
    border-radius: 4px;
    margin-bottom: 16px;
  }
  
  .summary p {
    margin: 0 0 4px 0;
  }
</style>
```

### 禁用和只读状态

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  
  let normalTime = 540; // 9:00
  let readonlyTime = 600; // 10:00
  let disabledTime = null;
</script>

<!-- 正常时间输入 -->
<TimeEditor bind:value={normalTime} />

<!-- 只读时间输入 -->
<TimeEditor bind:value={readonlyTime} readonly />

<!-- 禁用时间输入 -->
<TimeEditor bind:value={disabledTime} disabled />
```

### 计时器/秒表示例

```svelte
<script>
  import TimeEditor from "@ticatec/uniface-element/TimeEditor";
  import Button from "@ticatec/uniface-element/Button";
  
  let timerDuration = 5; // 5 分钟
  let currentTime = 0;
  let isRunning = false;
  let interval;
  
  function startTimer() {
    if (timerDuration > 0) {
      currentTime = timerDuration;
      isRunning = true;
      
      interval = setInterval(() => {
        currentTime -= 1/60; // 减去 1 秒（1/60 分钟）
        
        if (currentTime <= 0) {
          currentTime = 0;
          stopTimer();
          alert('计时器结束！');
        }
      }, 1000);
    }
  }
  
  function stopTimer() {
    isRunning = false;
    if (interval) {
      clearInterval(interval);
      interval = null;
    }
  }
  
  function resetTimer() {
    stopTimer();
    currentTime = 0;
  }
</script>

<div class="timer-container">
  <h3>计时器</h3>
  
  <div class="form-field">
    <label>设置持续时间：</label>
    <TimeEditor bind:value={timerDuration} precision="S" disabled={isRunning} />
  </div>
  
  <div class="form-field">
    <label>剩余时间：</label>
    <TimeEditor bind:value={currentTime} precision="S" readonly />
  </div>
  
  <div class="timer-controls">
    {#if !isRunning}
      <Button label="开始" onClick={startTimer} disabled={timerDuration <= 0} />
    {:else}
      <Button label="停止" onClick={stopTimer} />
    {/if}
    <Button label="重置" onClick={resetTimer} />
  </div>
</div>

<style>
  .timer-container {
    max-width: 300px;
    padding: 20px;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  .form-field {
    margin-bottom: 16px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
  
  .timer-controls {
    display: flex;
    gap: 8px;
  }
</style>
```

## 键盘导航

| 按键 | 操作 |
|-----|--------|
| `左箭头/上箭头` | 将光标移动到上一个时间段 |
| `右箭头/下箭头` | 将光标移动到下一个时间段 |
| `0-9` | 在当前光标位置输入数字 |
| `删除` | 清除当前时间段 |
| `退格` | 清除当前时间段并将光标后移 |
| `Tab` | 将焦点移动到下一个元素 |

## 时间格式

- **显示格式**：根据精度显示为 HH:MM 或 HH:MM:SS
- **存储格式**：表示总分钟数的数字值
- **范围**：00:00 至 23:59（0 至 1439 分钟）
- **秒**：以分钟的小数形式存储（30 秒 = 0.5 分钟）

## 行为

1. **焦点**：点击输入框聚焦并选中第一个时间段
2. **导航**：箭头键在小时、分钟和秒段之间移动
3. **输入**：数字键替换当前时间段的值
4. **验证**：自动拒绝无效时间
5. **清除**：清除按钮重置为空状态
6. **失焦**：验证并提交最终时间值

## 可访问性

- 在时间段之间提供适当的键盘导航
- 语义化 HTML 结构，兼容屏幕阅读器
- 当前时间段的焦点指示器
- 交互状态的清晰视觉反馈

## 最佳实践

1. **精度**：对大多数日程应用使用分钟精度
2. **验证**：如有需要，在更改处理程序中实现额外验证
3. **标签**：提供清晰的标签，描述预期的时间格式
4. **默认值**：考虑提供合理的默认时间
5. **范围验证**：在应用逻辑中验证时间范围

## 浏览器支持

- 支持完整输入事件的现代浏览器
- 兼容 Svelte 5+
- 支持键盘导航
- 触控友好界面

## 相关组件

- `DatePicker` - 日期选择组件
- `DateTimePicker` - 日期和时间组合选择
- `NumberEditor` - 数字输入验证
- `TextEditor` - 基本文本输入组件