# DateRangeEditor 组件

一个基于 Svelte 构建的全面日期范围选择组件，提供直观的界面来选择开始和结束日期，具有双日历弹出功能。

## 特性

- **双日历界面**：并排显示的日历用于开始/结束日期选择
- **智能日期约束**：开始和结束日期之间的自动最小/最大值验证
- **多种视觉变体**：支持 plain、outlined、filled 和 compact 样式
- **只读输入字段**：防止手动文本输入，确保数据完整性
- **灵活的日期格式**：可自定义日期显示格式
- **清除功能**：集成清除按钮，轻松重置范围
- **弹窗管理**：点击外部关闭的行为
- **响应式设计**：自动调整弹窗容器大小
- **集成就绪**：与表单布局无缝协作

## 安装

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

```typescript
import DateRangeEditor from '@ticatec/uniface-element/DateRange';
```

或者在同一项目中使用：
```typescript
import DateRangeEditor from '$lib/date-range';
```

## 基本用法

```svelte
<script>
  import DateRangeEditor from '$lib/date-range';
  
  let fromDate = null;
  let toDate = null;
</script>

<DateRangeEditor 
  bind:fromValue={fromDate} 
  bind:toValue={toDate} 
  placeholder="选择日期范围" 
/>
```

## 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `fromValue` | `UniDate` | `null` | 范围的开始日期 |
| `toValue` | `UniDate` | `null` | 范围的结束日期 |
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉样式变体 |
| `compact` | `boolean` | `false` | 紧凑显示模式 |
| `style` | `string` | `''` | 自定义 CSS 样式 |
| `class` | `string` | `''` | 附加的 CSS 类 |
| `format` | `string` | `'YYYY-MM-DD'` | 日期显示格式 |
| `min` | `string \| Date \| null` | `null` | 最小可选日期 |
| `max` | `string \| Date \| null` | `null` | 最大可选日期 |

## 示例

### 基本日期范围选择器

```svelte
<script>
  import DateRangeEditor from '$lib/date-range';
  
  let startDate = null;
  let endDate = null;
</script>

<DateRangeEditor 
  bind:fromValue={startDate} 
  bind:toValue={endDate} 
/>
```

### 带自定义格式的轮廓日期范围

```svelte
<DateRangeEditor 
  variant="outlined"
  bind:fromValue={projectStart} 
  bind:toValue={projectEnd}
  format="DD/MM/YYYY"
/>
```

### 填充紧凑日期范围

```svelte
<DateRangeEditor 
  variant="filled"
  compact
  bind:fromValue={reportFrom} 
  bind:toValue={reportTo}
/>
```

### 带最小/最大约束的日期范围

```svelte
<script>
  // 只允许当前年份内的日期
  const currentYear = new Date().getFullYear();
  const yearStart = new Date(currentYear, 0, 1);
  const yearEnd = new Date(currentYear, 11, 31);
</script>

<DateRangeEditor 
  variant="outlined"
  bind:fromValue={periodStart} 
  bind:toValue={periodEnd}
  min={yearStart}
  max={yearEnd}
/>
```

### 自定义样式的日期范围

```svelte
<DateRangeEditor 
  variant="filled"
  style="width: 300px; margin: 10px;"
  class="custom-date-range"
  bind:fromValue={eventStart} 
  bind:toValue={eventEnd}
  format="MMM DD, YYYY"
/>
```

## 智能日期验证

组件自动执行逻辑日期约束：

- **开始日期最大值**：不能超过选择的"结束"日期或全局 `max` 值
- **结束日期最小值**：不能早于选择的"开始"日期或全局 `min` 值
- **全局约束**：两个日期都遵守 `min` 和 `max` 属性

```svelte
<script>
  let checkIn = new Date('2024-01-15');
  let checkOut = null; // 将被约束为 checkIn 之后的日期
</script>

<DateRangeEditor 
  bind:fromValue={checkIn} 
  bind:toValue={checkOut}
  min={new Date()} // 不允许过去的日期
/>
```

## 日期格式

`format` 属性支持 Day.js 格式化标记：

| 格式 | 示例 | 描述 |
|--------|---------|-------------|
| `YYYY-MM-DD` | 2024-03-15 | ISO 格式（默认） |
| `DD/MM/YYYY` | 15/03/2024 | 欧洲格式 |
| `MM/DD/YYYY` | 03/15/2024 | 美国格式 |
| `MMM DD, YYYY` | Mar 15, 2024 | 长月份名称 |
| `DD MMM YYYY` | 15 Mar 2024 | 日-月-年 |

```svelte
<!-- 不同格式示例 -->
<DateRangeEditor format="DD/MM/YYYY" bind:fromValue={start} bind:toValue={end} />
<DateRangeEditor format="MMM DD, YYYY" bind:fromValue={start} bind:toValue={end} />
<DateRangeEditor format="DD MMM YYYY" bind:fromValue={start} bind:toValue={end} />
```

## 日历交互

### 双日历布局

弹窗显示两个并排的日历：
- **左日历**：用于选择"开始"日期
- **右日历**：用于选择"结束"日期
- **智能约束**：每个日历根据另一个的选择来遵守约束

### 选择工作流程

1. 点击组件任何地方打开弹窗
2. 从左日历选择开始日期
3. 从右日历选择结束日期（自动关闭弹窗）
4. 使用清除按钮（×）重置两个日期

## 变体

### 默认（`''`）
标准边框和背景的基本样式。

```svelte
<DateRangeEditor bind:fromValue={start} bind:toValue={end} />
```

### 简约（`'plain'`）
减少视觉元素的最小样式。

```svelte
<DateRangeEditor variant="plain" bind:fromValue={start} bind:toValue={end} />
```

### 轮廓（`'outlined'`）
突出的边框样式以强调。

```svelte
<DateRangeEditor variant="outlined" bind:fromValue={start} bind:toValue={end} />
```

### 填充（`'filled'`）
背景填充样式以提供更好的视觉层次。

```svelte
<DateRangeEditor variant="filled" bind:fromValue={start} bind:toValue={end} />
```

## 与表单集成

DateRangeEditor 与表单布局和 FormField 组件无缝协作：

```svelte
<script>
  import FormField from '$lib/form-field';
  import DateRangeEditor from '$lib/date-range';
</script>

<FormField label="项目持续时间">
  <DateRangeEditor 
    variant="outlined"
    bind:fromValue={projectStart} 
    bind:toValue={projectEnd}
  />
</FormField>

<FormField label="报告期间">
  <DateRangeEditor 
    variant="filled"
    format="MMM DD, YYYY"
    bind:fromValue={reportStart} 
    bind:toValue={reportEnd}
  />
</FormField>
```

## 样式定制

组件继承 Uniface 设计系统的样式，可以自定义：

```css
.custom-date-range {
  --uniface-inside-border-color: #e0e0e0;
  --uniface-primary-color: #1976d2;
}

.uniface-range-editor {
  /* 范围编辑器的自定义样式 */
}

.date-popover {
  /* 弹窗的自定义样式 */
}
```

## 无障碍访问

- **键盘导航**：通过 Tab 在输入字段间切换，使用 Enter/Space 打开弹窗
- **ARIA 支持**：为屏幕阅读器提供适当的标签和描述
- **焦点管理**：逻辑焦点流和清晰的视觉指示器
- **只读输入**：防止无效的手动输入，同时保持无障碍访问

## 事件处理

```svelte
<script>
  import DateRangeEditor from '$lib/date-range';
  
  let fromDate = null;
  let toDate = null;
  
  // 响应变化
  $: if (fromDate && toDate) {
    console.log('选择的日期范围：', fromDate, '到', toDate);
    calculateDateDifference();
  }
  
  function calculateDateDifference() {
    if (fromDate && toDate) {
      const diffTime = Math.abs(toDate - fromDate);
      const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
      console.log(`持续时间：${diffDays} 天`);
    }
  }
</script>

<DateRangeEditor 
  bind:fromValue={fromDate} 
  bind:toValue={toDate}
  on:blur={() => console.log('范围编辑器失去焦点')}
/>
```

## 类型定义

```typescript
type UniDate = Date | dayjs.Dayjs | string | null;

interface DateRangeEditorProps {
  fromValue: UniDate;
  toValue: UniDate;
  variant?: '' | 'plain' | 'outlined' | 'filled';
  compact?: boolean;
  style?: string;
  class?: string;
  format?: string;
  min?: string | Date | null;
  max?: string | Date | null;
}
```

## 常见用例

### 项目时间线选择
```svelte
<DateRangeEditor 
  variant="outlined"
  bind:fromValue={projectStart} 
  bind:toValue={projectEnd}
  format="MMM DD, YYYY"
/>
```

### 预订日期范围
```svelte
<DateRangeEditor 
  variant="filled"
  bind:fromValue={checkIn} 
  bind:toValue={checkOut}
  min={new Date()} // 不允许过去的日期
/>
```

### 报告期间筛选
```svelte
<DateRangeEditor 
  compact
  bind:fromValue={reportFrom} 
  bind:toValue={reportTo}
  format="DD/MM/YYYY"
/>
```

### 事件持续时间
```svelte
<DateRangeEditor 
  variant="outlined"
  bind:fromValue={eventStart} 
  bind:toValue={eventEnd}
  min={new Date()}
  max={new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)} // 最多提前1年
/>
```

## 注意事项

- 组件内部使用 Day.js 进行日期操作和格式化
- 输入字段为只读，防止无效的手动输入
- 弹窗自动定位以适应视口
- 只有当至少选择了一个日期时才显示清除功能
- 日期约束在选择时实时执行
- 组件维护弹窗可见性和约束的内部状态