# 密码编辑器组件

一个专为密码输入设计的组件，带有可见性切换功能。允许用户显示/隐藏密码文本以提升可用性，同时保持安全性。基于 `CommonEditor` 基础组件构建。

## 安装

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

## 导入

```typescript
import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
```

## 基本用法

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let password = '';
</script>

<PasswordEditor 
  bind:value={password} 
  placeholder="输入密码..."
/>
```

## 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `value` | `string` | `""` | 当前密码值 |
| `disabled` | `boolean` | `false` | 输入框是否禁用 |
| `readonly` | `boolean` | `false` | 输入框是否只读 |
| `variant` | `"" \| "plain" \| "outlined" \| "filled"` | `""` | 输入框的视觉变体 |
| `compact` | `boolean` | `false` | 是否使用紧凑间距 |
| `style` | `string` | `""` | 附加 CSS 样式 |
| `class` | `string` | `""` | CSS 类名 |
| `onfocus` | `((event: FocusEvent) => void) \| null` | `null` | 输入框获得焦点时的回调函数 |
| `onblur` | `((event: FocusEvent) => void) \| null` | `null` | 输入框失去焦点时的回调函数 |
| `onchange` | `((event: Event) => void) \| null` | `null` | 值更改时的回调函数 |

## 事件

| 事件 | 描述 |
|-------|-------------|
| `focus` | 输入框获得焦点时触发 |
| `blur` | 输入框失去焦点时触发 |

## 示例

### 基本密码输入

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let password = '';
  let confirmPassword = '';
</script>

<div class="form-group">
  <label>密码：</label>
  <PasswordEditor bind:value={password} placeholder="输入密码" />
</div>

<div class="form-group">
  <label>确认密码：</label>
  <PasswordEditor bind:value={confirmPassword} placeholder="确认密码" />
</div>

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

### 带事件处理程序

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let password = '';
  let isFocused = false;
  
  function handleChange(event) {
    console.log('密码已更改');
  }
  
  function handleFocus(event) {
    console.log('密码输入框已聚焦');
    isFocused = true;
  }
  
  function handleBlur(event) {
    console.log('密码输入框已失焦');
    isFocused = false;
  }
</script>

<PasswordEditor 
  bind:value={password}
  placeholder="输入安全密码..."
  onchange={handleChange}
  onfocus={handleFocus}
  onblur={handleBlur}
/>

{#if isFocused}
  <p>密码字段处于活动状态</p>
{/if}
```

### 不同变体

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let pass1 = '', pass2 = '', pass3 = '';
</script>

<!-- 默认变体 -->
<PasswordEditor bind:value={pass1} placeholder="默认密码" />

<!-- 轮廓变体 -->
<PasswordEditor bind:value={pass2} variant="outlined" placeholder="轮廓密码" />

<!-- 填充变体 -->
<PasswordEditor bind:value={pass3} variant="filled" placeholder="填充密码" />
```

### 禁用和只读状态

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let normalPassword = '';
  let readonlyPassword = '只读密码';
  let disabledPassword = '';
</script>

<!-- 正常密码输入 -->
<PasswordEditor bind:value={normalPassword} placeholder="正常密码" />

<!-- 只读密码输入 -->
<PasswordEditor bind:value={readonlyPassword} readonly />

<!-- 禁用密码输入 -->
<PasswordEditor bind:value={disabledPassword} disabled placeholder="禁用密码" />
```

### 紧凑模式

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let compactPassword = '';
</script>

<div class="login-form">
  <PasswordEditor 
    bind:value={compactPassword} 
    compact 
    placeholder="密码" 
  />
</div>

<style>
  .login-form {
    max-width: 300px;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 8px;
  }
</style>
```

### 登录表单示例

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  import TextEditor from "@ticatec/uniface-element/TextEditor";
  import Button from "@ticatec/uniface-element/Button";
  
  let credentials = {
    username: '',
    password: ''
  };
  
  function handleLogin() {
    if (credentials.username && credentials.password) {
      console.log('正在登录...', credentials.username);
      // 执行登录逻辑
    }
  }
  
  function handleKeydown(event) {
    if (event.key === 'Enter') {
      handleLogin();
    }
  }
</script>

<div class="login-container">
  <h2>登录</h2>
  
  <div class="form-field">
    <label>用户名</label>
    <TextEditor 
      bind:value={credentials.username} 
      placeholder="输入用户名"
      on:keydown={handleKeydown}
    />
  </div>
  
  <div class="form-field">
    <label>密码</label>
    <PasswordEditor 
      bind:value={credentials.password} 
      placeholder="输入密码"
      on:keydown={handleKeydown}
    />
  </div>
  
  <Button 
    type="primary" 
    label="登录" 
    onClick={handleLogin}
    disabled={!credentials.username || !credentials.password}
  />
</div>

<style>
  .login-container {
    max-width: 400px;
    margin: 0 auto;
    padding: 24px;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  h2 {
    text-align: center;
    margin-bottom: 24px;
  }
  
  .form-field {
    margin-bottom: 16px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
</style>
```

### 密码强度指示器

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  
  let password = '';
  
  $: passwordStrength = calculateStrength(password);
  
  function calculateStrength(pass) {
    if (!pass) return { score: 0, label: '', color: '' };
    
    let score = 0;
    if (pass.length >= 8) score++;
    if (/[a-z]/.test(pass)) score++;
    if (/[A-Z]/.test(pass)) score++;
    if (/[0-9]/.test(pass)) score++;
    if (/[^A-Za-z0-9]/.test(pass)) score++;
    
    const labels = ['', '弱', '一般', '良好', '强', '非常强'];
    const colors = ['', '#ff4444', '#ff8800', '#ffaa00', '#88cc00', '#44cc00'];
    
    return {
      score,
      label: labels[score],
      color: colors[score]
    };
  }
</script>

<div class="password-container">
  <label>创建密码</label>
  <PasswordEditor bind:value={password} placeholder="输入一个强密码" />
  
  {#if password}
    <div class="strength-indicator">
      <div class="strength-bar">
        <div 
          class="strength-fill" 
          style="width: {passwordStrength.score * 20}%; background-color: {passwordStrength.color}"
        ></div>
      </div>
      <span class="strength-label" style="color: {passwordStrength.color}">
        {passwordStrength.label}
      </span>
    </div>
  {/if}
  
  <div class="password-requirements">
    <p>密码应包含：</p>
    <ul>
      <li class:met={password.length >= 8}>至少 8 个字符</li>
      <li class:met={/[a-z]/.test(password)}>小写字母</li>
      <li class:met={/[A-Z]/.test(password)}>大写字母</li>
      <li class:met={/[0-9]/.test(password)}>数字</li>
      <li class:met={/[^A-Za-z0-9]/.test(password)}>特殊字符</li>
    </ul>
  </div>
</div>

<style>
  .password-container {
    max-width: 400px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
  
  .strength-indicator {
    margin-top: 8px;
    display: flex;
    align-items: center;
    gap: 12px;
  }
  
  .strength-bar {
    flex: 1;
    height: 4px;
    background: #e0e0e0;
    border-radius: 2px;
    overflow: hidden;
  }
  
  .strength-fill {
    height: 100%;
    transition: width 0.3s ease, background-color 0.3s ease;
  }
  
  .strength-label {
    font-size: 12px;
    font-weight: 500;
  }
  
  .password-requirements {
    margin-top: 12px;
    font-size: 14px;
  }
  
  .password-requirements p {
    margin: 0 0 8px 0;
    font-weight: 500;
  }
  
  .password-requirements ul {
    margin: 0;
    padding-left: 20px;
  }
  
  .password-requirements li {
    color: #666;
    transition: color 0.3s ease;
  }
  
  .password-requirements li.met {
    color: #44cc00;
  }
</style>
```

### 注册表单

```svelte
<script>
  import { PasswordEditor } from "@ticatec/uniface-element/TextEditor";
  import TextEditor from "@ticatec/uniface-element/TextEditor";
  import Button from "@ticatec/uniface-element/Button";
  
  let formData = {
    email: '',
    password: '',
    confirmPassword: ''
  };
  
  $: passwordsMatch = formData.password && 
    formData.confirmPassword && 
    formData.password === formData.confirmPassword;
  
  $: isValid = formData.email && 
    formData.password && 
    passwordsMatch;
  
  function handleRegister() {
    if (isValid) {
      console.log('正在注册用户...', formData.email);
      // 执行注册逻辑
    }
  }
</script>

<div class="register-container">
  <h2>创建账户</h2>
  
  <div class="form-field">
    <label>邮箱</label>
    <TextEditor 
      bind:value={formData.email} 
      placeholder="输入邮箱地址"
    />
  </div>
  
  <div class="form-field">
    <label>密码</label>
    <PasswordEditor 
      bind:value={formData.password} 
      placeholder="创建密码"
    />
  </div>
  
  <div class="form-field">
    <label>确认密码</label>
    <PasswordEditor 
      bind:value={formData.confirmPassword} 
      placeholder="确认密码"
    />
    {#if formData.confirmPassword && !passwordsMatch}
      <div class="error-message">密码不匹配</div>
    {/if}
  </div>
  
  <Button 
    type="primary" 
    label="创建账户" 
    onClick={handleRegister}
    disabled={!isValid}
  />
</div>

<style>
  .register-container {
    max-width: 400px;
    margin: 0 auto;
    padding: 24px;
    border: 1px solid #ddd;
    border-radius: 8px;
  }
  
  h2 {
    text-align: center;
    margin-bottom: 24px;
  }
  
  .form-field {
    margin-bottom: 16px;
  }
  
  label {
    display: block;
    margin-bottom: 4px;
    font-weight: 500;
  }
  
  .error-message {
    margin-top: 4px;
    color: #ff4444;
    font-size: 12px;
  }
</style>
```

## 功能

- **可见性切换**：内置眼睛图标，用于显示/隐藏密码文本
- **安全性**：默认保持密码掩码
- **事件处理**：自定义焦点、失焦和更改事件回调
- **灵活样式**：支持多种视觉变体和自定义样式
- **状态管理**：支持禁用和只读状态
- **响应式**：适配所有设备尺寸

## 行为

1. **默认状态**：密码文本默认隐藏（掩码）
2. **切换可见性**：点击眼睛图标显示/隐藏密码
3. **禁用/只读**：禁用或只读时隐藏眼睛图标
4. **焦点管理**：为可访问性提供适当的焦点处理
5. **输入类型切换**：在 `password` 和 `text` 输入类型之间动态切换

## 安全注意事项

- 默认掩码密码文本以确保安全
- 可见性切换有助于提高密码输入准确性
- 组件不记录或暴露密码内容
- 遵循标准密码输入安全实践

## 可访问性

- 适当的语义化 HTML 结构
- 支持键盘导航
- 屏幕阅读器兼容
- 焦点指示器和管理
- 交互状态的清晰视觉反馈

## 最佳实践

1. **密码要求**：始终提供明确的密码要求
2. **确认字段**：为新密码创建使用确认字段
3. **强度指示器**：考虑添加密码强度反馈
4. **错误处理**：提供清晰的验证消息
5. **安全性**：切勿以明文存储或记录密码值
6. **可访问性**：确保适当的标签和键盘导航

## 浏览器支持

- 支持完整输入事件的现代浏览器
- 兼容 Svelte 5+
- 触控友好界面
- 响应式设计原则

## 相关组件

- `TextEditor` - 基本文本输入组件
- `SearchBox` - 专用搜索输入
- `NumberEditor` - 数字输入验证
- `Button` - 用于表单提交和操作