# GroupCheckBox 组复选框组件

一个灵活的组复选框组件，允许用户从预定义的选项列表中选择多个选项。支持基于字符串和基于位的值存储、自定义样式变体以及全面的状态管理，包括焦点/失焦事件处理。

## 安装

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

## 导入

```typescript
import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
import { DisplayMode } from "@ticatec/uniface-element";
import type { OnChangeHandler } from "@ticatec/uniface-element";
```

## 基本用法

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  
  const options = [
    { code: "js", text: "JavaScript" },
    { code: "ts", text: "TypeScript" },
    { code: "py", text: "Python" },
    { code: "go", text: "Go" }
  ];
  
  let selectedLanguages = "";
  
  function handleChange(value) {
    console.log('选择的编程语言:', value);
  }
</script>

<GroupCheckBox 
  {options} 
  bind:value={selectedLanguages}
  onchange={handleChange}
/>
```

## 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `value` | `string \| number` | 必需 | 选中的值（带分隔符的字符串或位值） |
| `options` | `Array<any>` | 必需 | 选项对象数组 |
| `keyField` | `string` | `"code"` | 选项键/值的字段名称 |
| `textField` | `string` | `"text"` | 选项显示文本的字段名称 |
| `delimiter` | `string` | `";"` | 基于字符串的值的分隔符 |
| `bitBase` | `boolean` | `false` | 是否使用基于位的值存储 |
| `disabled` | `boolean` | `false` | 组件是否禁用 |
| `readonly` | `boolean` | `false` | 组件是否只读 |
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式（编辑或查看） |
| `variant` | `"" \| "plain" \| "outlined" \| "filled"` | `""` | 视觉变体 |
| `compact` | `boolean` | `false` | 是否使用紧凑间距 |
| `disabledOptions` | `Array<string>` | `[]` | 要禁用的选项键数组 |
| `hideOptions` | `Array<string>` | `[]` | 要隐藏的选项键数组 |
| `style` | `string` | `""` | 附加 CSS 样式 |
| `item$style` | `string` | `""` | 单个复选框项目的 CSS 样式 |
| `onchange` | `OnChangeHandler<string \| number>` | `null` | 更改事件处理程序 |
| `onfocus` | `(() => void) \| null` | `null` | 焦点事件处理程序 |
| `onblur` | `(() => void) \| null` | `null` | 失焦事件处理程序 |

## 方法

| 方法 | 描述 |
|--------|-------------|
| `setFocus()` | 以编程方式聚焦第一个启用的复选框 |

## 示例

### 基于字符串的选择

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const technologies = [
    { code: "react", text: "React" },
    { code: "vue", text: "Vue.js" },
    { code: "svelte", text: "Svelte" },
    { code: "angular", text: "Angular" }
  ];
  
  let selectedTech = "";
  
  function handleTechChange(value) {
    console.log('选择的技术:', value);
    console.log('数组形式:', value ? value.split(';') : []);
  }
</script>

<div class="demo-section">
  <FormField label="选择您偏好的技术">
    <GroupCheckBox 
      options={technologies}
      bind:value={selectedTech}
      onchange={handleTechChange}
      onfocus={() => console.log('获得焦点')}
      onblur={() => console.log('失去焦点')}
    />
  </FormField>
  
  <p>已选择: {selectedTech || '无'}</p>
</div>

<style>
  .demo-section {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### 基于位的选择以提高性能

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const permissions = [
    { code: "read", text: "读取" },      // 位 0（值 1）
    { code: "write", text: "写入" },    // 位 1（值 2）
    { code: "delete", text: "删除" },  // 位 2（值 4）
    { code: "admin", text: "管理员" }     // 位 3（值 8）
  ];
  
  let userPermissions = 0;
  
  function handlePermissionChange(value) {
    console.log('权限位:', value);
    console.log('二进制表示:', value.toString(2));
    
    // 解码权限
    const activePermissions = [];
    permissions.forEach((perm, index) => {
      if (value & (1 << index)) {
        activePermissions.push(perm.text);
      }
    });
    console.log('活跃权限:', activePermissions);
  }
  
  // 检查特定权限是否启用
  function hasPermission(permissionBit) {
    return (userPermissions & (1 << permissionBit)) !== 0;
  }
</script>

<div class="permissions-demo">
  <FormField label="用户权限">
    <GroupCheckBox 
      options={permissions}
      bitBase={true}
      bind:value={userPermissions}
      onchange={handlePermissionChange}
    />
  </FormField>
  
  <div class="permission-info">
    <p>权限值: {userPermissions}</p>
    <p>二进制: {userPermissions.toString(2).padStart(4, '0')}</p>
    <div class="permission-checks">
      <p>可读取: {hasPermission(0) ? '✓' : '✗'}</p>
      <p>可写入: {hasPermission(1) ? '✓' : '✗'}</p>
      <p>可删除: {hasPermission(2) ? '✓' : '✗'}</p>
      <p>是管理员: {hasPermission(3) ? '✓' : '✗'}</p>
    </div>
  </div>
</div>

<style>
  .permissions-demo {
    margin: 20px;
    padding: 20px;
    background: #f8fafc;
    border-radius: 8px;
  }
  
  .permission-info {
    margin-top: 16px;
    padding: 12px;
    background: white;
    border-radius: 6px;
  }
  
  .permission-checks {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-top: 8px;
  }
</style>
```

### 不同视觉变体

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const colors = [
    { code: "red", text: "红色" },
    { code: "blue", text: "蓝色" },
    { code: "green", text: "绿色" },
    { code: "yellow", text: "黄色" }
  ];
  
  let selectedColors = "";
</script>

<div class="variants-demo">
  <div class="variant-section">
    <h3>默认变体</h3>
    <FormField label="选择颜色">
      <GroupCheckBox options={colors} bind:value={selectedColors} />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>轮廓变体</h3>
    <FormField label="选择颜色">
      <GroupCheckBox 
        options={colors} 
        variant="outlined"
        bind:value={selectedColors} 
      />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>填充变体</h3>
    <FormField label="选择颜色">
      <GroupCheckBox 
        options={colors} 
        variant="filled"
        bind:value={selectedColors} 
      />
    </FormField>
  </div>
  
  <div class="variant-section">
    <h3>紧凑变体</h3>
    <FormField label="选择颜色">
      <GroupCheckBox 
        options={colors} 
        compact={true}
        bind:value={selectedColors} 
      />
    </FormField>
  </div>
</div>

<style>
  .variants-demo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin: 20px;
  }
  
  .variant-section {
    padding: 16px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### 条件选项和动态行为

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const airlines = [
    { code: "none", text: "无" },
    { code: "aa", text: "中国国际航空" },
    { code: "ua", text: "中国东方航空" },
    { code: "dl", text: "南方航空" },
    { code: "sw", text: "海南航空" }
  ];
  
  let selectedAirlines = "";
  let disabledOptions = [];
  let hideOptions = [];
  
  function handleAirlineChange(value) {
    const selected = value ? value.split(';') : [];
    
    // 如果选择了“无”，禁用其他选项
    if (selected.includes('none')) {
      if (selected.length > 1) {
        // 如果“无”与其他选项一起被选中，仅保留“无”
        selectedAirlines = "none";
      }
      disabledOptions = airlines
        .filter(airline => airline.code !== 'none')
        .map(airline => airline.code);
    } else {
      disabledOptions = [];
    }
    
    // 周末隐藏海南航空（演示逻辑）
    const today = new Date();
    if (today.getDay() === 0 || today.getDay() === 6) {
      hideOptions = ['sw'];
    } else {
      hideOptions = [];
    }
  }
  
  // 处理特殊的“无”选择逻辑
  function handleSpecialLogic(value) {
    const selected = value ? value.split(';') : [];
    
    if (selected.includes('none') && selected.length > 1) {
      // 如果选择了其他选项，移除“无”
      const filteredSelected = selected.filter(option => option !== 'none');
      selectedAirlines = filteredSelected.join(';');
    } else {
      handleAirlineChange(value);
    }
  }
</script>

<div class="conditional-demo">
  <FormField label="选择偏好的航空公司">
    <GroupCheckBox 
      options={airlines}
      bind:value={selectedAirlines}
      {disabledOptions}
      {hideOptions}
      onchange={handleSpecialLogic}
    />
  </FormField>
  
  <div class="info-panel">
    <p><strong>已选择:</strong> {selectedAirlines || '无'}</p>
    <p><strong>已禁用:</strong> {disabledOptions.join(', ') || '无'}</p>
    <p><strong>已隐藏:</strong> {hideOptions.join(', ') || '无'}</p>
    <p class="hint">💡 选择“无”以禁用其他选项</p>
    {#if hideOptions.includes('sw')}
      <p class="weekend-notice">🚫 周末不可选择海南航空</p>
    {/if}
  </div>
</div>

<style>
  .conditional-demo {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .info-panel {
    margin-top: 16px;
    padding: 12px;
    background: #f8fafc;
    border-radius: 6px;
  }
  
  .hint {
    color: #6b7280;
    font-style: italic;
  }
  
  .weekend-notice {
    color: #dc2626;
    font-weight: 500;
  }
</style>
```

### 只读和显示模式

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  import { DisplayMode } from "@ticatec/uniface-element";
  
  const skills = [
    { code: "js", text: "JavaScript" },
    { code: "ts", text: "TypeScript" },
    { code: "react", text: "React" },
    { code: "node", text: "Node.js" }
  ];
  
  let userSkills = "js;react;node";  // 预选技能
</script>

<div class="readonly-demo">
  <div class="mode-section">
    <h3>可编辑模式</h3>
    <FormField label="编辑您的技能">
      <GroupCheckBox 
        options={skills}
        bind:value={userSkills}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>只读模式</h3>
    <FormField label="您的技能（只读）">
      <GroupCheckBox 
        options={skills}
        value={userSkills}
        readonly={true}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>禁用模式</h3>
    <FormField label="您的技能（禁用）">
      <GroupCheckBox 
        options={skills}
        value={userSkills}
        disabled={true}
      />
    </FormField>
  </div>
  
  <div class="mode-section">
    <h3>仅显示模式</h3>
    <FormField label="您的技能（显示）">
      <GroupCheckBox 
        options={skills}
        value={userSkills}
        displayMode={DisplayMode.View}
      />
    </FormField>
  </div>
</div>

<style>
  .readonly-demo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
    margin: 20px;
  }
  
  .mode-section {
    padding: 16px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
</style>
```

### 自定义样式和字段配置

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const departments = [
    { id: "eng", name: "工程部", active: true },
    { id: "sales", name: "销售部", active: true },
    { id: "marketing", name: "市场部", active: false },
    { id: "hr", name: "人力资源部", active: true }
  ];
  
  let selectedDepartments = "";
</script>

<div class="custom-demo">
  <FormField label="选择部门">
    <GroupCheckBox 
      options={departments}
      keyField="id"
      textField="name"
      delimiter=","
      bind:value={selectedDepartments}
      variant="outlined"
      style="border: 2px solid #3b82f6; border-radius: 12px; padding: 16px;"
      item$style="margin: 8px 0; padding: 4px 8px; background: #f0f9ff; border-radius: 6px;"
    />
  </FormField>
  
  <div class="result">
    <p><strong>选择的部门:</strong></p>
    <p>原始值: {selectedDepartments}</p>
    <p>数组形式: {JSON.stringify(selectedDepartments ? selectedDepartments.split(',') : [])}</p>
  </div>
</div>

<style>
  .custom-demo {
    margin: 20px;
    padding: 20px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .result {
    margin-top: 16px;
    padding: 12px;
    background: #f8fafc;
    border-radius: 6px;
  }
</style>
```

### 表单集成与验证

```svelte
<script>
  import GroupCheckBox from "@ticatec/uniface-element/GroupCheckBox";
  import FormField from "@ticatec/uniface-element/FormField";
  
  const interests = [
    { code: "sports", text: "运动" },
    { code: "music", text: "音乐" },
    { code: "travel", text: "旅行" },
    { code: "cooking", text: "烹饪" },
    { code: "reading", text: "阅读" },
    { code: "gaming", text: "游戏" }
  ];
  
  let formData = {
    name: '',
    interests: '',
    newsletter: false
  };
  
  let errors = {};
  let groupCheckBoxRef;
  
  function validateForm() {
    errors = {};
    
    if (!formData.name.trim()) {
      errors.name = '姓名是必填项';
    }
    
    if (!formData.interests) {
      errors.interests = '请至少选择一个兴趣';
    } else {
      const selectedCount = formData.interests.split(';').length;
      if (selectedCount > 3) {
        errors.interests = '最多选择3个兴趣';
      }
    }
    
    return Object.keys(errors).length === 0;
  }
  
  function handleSubmit() {
    if (validateForm()) {
      console.log('表单提交:', formData);
      alert('表单提交成功！');
    } else {
      console.log('验证错误:', errors);
      // 聚焦第一个错误字段
      if (errors.interests) {
        groupCheckBoxRef.setFocus();
      }
    }
  }
  
  function handleInterestChange(value) {
    formData.interests = value;
    // 用户选择时清除错误
    if (errors.interests && value) {
      delete errors.interests;
      errors = { ...errors };
    }
  }
</script>

<div class="form-demo">
  <form on:submit|preventDefault={handleSubmit}>
    <FormField label="您的姓名" error={errors.name}>
      <input 
        type="text" 
        bind:value={formData.name}
        class="form-input"
        class:error={errors.name}
      />
    </FormField>
    
    <FormField label="您的兴趣（选择1-3项）" error={errors.interests}>
      <GroupCheckBox 
        bind:this={groupCheckBoxRef}
        options={interests}
        bind:value={formData.interests}
        onchange={handleInterestChange}
        variant="outlined"
      />
      <p class="field-hint">选择最多3个最能描述您的兴趣</p>
    </FormField>
    
    <FormField label="订阅简讯">
      <label class="checkbox-label">
        <input type="checkbox" bind:checked={formData.newsletter} />
        订阅我们的简讯
      </label>
    </FormField>
    
    <div class="form-actions">
      <button type="submit" class="submit-btn">提交</button>
      <button type="button" on:click={() => console.log(formData)} class="preview-btn">
        预览数据
      </button>
    </div>
  </form>
</div>

<style>
  .form-demo {
    max-width: 500px;
    margin: 20px;
    padding: 24px;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
  }
  
  .form-input {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
  }
  
  .form-input.error {
    border-color: #dc2626;
  }
  
  .field-hint {
    margin-top: 4px;
    font-size: 0.875rem;
    color: #6b7280;
  }
  
  .checkbox-label {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
  }
  
  .form-actions {
    display: flex;
    gap: 12px;
    margin-top: 20px;
  }
  
  .submit-btn, .preview-btn {
    padding: 10px 20px;
    border-radius: 6px;
    border: none;
    cursor: pointer;
  }
  
  .submit-btn {
    background: #3b82f6;
    color: white;
  }
  
  .preview-btn {
    background: #f3f4f6;
    color: #374151;
    border: 1px solid #d1d5db;
  }
</style>
```

## 值格式

### 基于字符串的值
当 `bitBase` 为 `false`（默认值）时，选中的值存储为带分隔符的字符串：
- 单一选择：`"option1"`
- 多项选择：`"option1;option2;option3"`
- 无选择：`""`

### 基于位的值
当 `bitBase` 为 `true` 时，选中的值存储为一个数字，每个位表示一个选项：
- 第一个选项（索引0）：位0（值1）
- 第二个选项（索引1）：位1（值2）
- 第三个选项（索引2）：位2（值4）
- 组合：第一和第三个选项 = 1 + 4 = 5

## 样式

GroupCheckBox 组件可通过 CSS 自定义属性进行样式化：

```css
.uniface-group-box {
  --checkbox-spacing: 8px;
  --checkbox-padding: 4px 8px;
  --border-color: #e2e8f0;
  --background-color: #ffffff;
  --hover-background: #f9fafb;
}

/* 变体特定样式 */
.uniface-group-box.outlined {
  border: 1px solid var(--border-color);
  border-radius: 6px;
  padding: 12px;
}

.uniface-group-box.filled {
  background-color: #f8fafc;
  border-radius: 6px;
  padding: 12px;
}

.uniface-group-box.compact {
  --checkbox-spacing: 4px;
  --checkbox-padding: 2px 4px;
}
```

## 可访问性

GroupCheckBox 组件包括以下可访问性功能：

- 复选框之间的键盘导航
- 使用 `setFocus()` 方法进行焦点管理
- ARIA 标签和描述
- 状态变化的屏幕阅读器提示
- 适当的 Tab 键顺序管理

## 最佳实践

1. **选项管理**：保持选项数组的稳定性以避免不必要的重新渲染
2. **性能**：对于大型数据集或性能关键场景，使用基于位的值
3. **验证**：为必选项目实现适当的验证
4. **用户体验**：为禁用/隐藏选项提供清晰的反馈
5. **可访问性**：始终提供有意义的标签和描述
6. **状态管理**：适当地处理焦点/失焦事件以进行表单验证

