# HyperOS 组件开发规范

> 新建组件流程、Props 命名约定（checked/onChange/onPress/leadingIcon 等）、类型定义规范（默认不用 enum）、实现规范（referenceReport/colorToken/无障碍）、测试规范、导出检查清单。

## 新建组件流程

1. 在 `src/hyperOS/components/`（基础组件）或 `src/hyperOS/IotComponents/`（IoT 组件）下创建组件文件夹
2. 创建 `组件名.tsx`、`interface.ts`（Props 类型）、`index.tsx`（barrel 导出）
3. 在 `src/hyperOS/index.ts` 中添加导出
4. 在 `importMap.js` 中添加映射
5. 创建 `__tests__/` 目录，使用 `@testing-library/react-native` 编写测试
6. 创建 `组件名.zh-CN.md` 文档，并按 `component-template.md` 模板填写组件元数据
7. 在 `docs/components.md` 中补充公共 API 摘要；必要时同步更新 `docs/architecture.md` 和设计 Token 文档

### 文件夹结构模板

```
src/hyperOS/components/myComponent/
  MyComponent.tsx          # 组件实现
  interface.ts             # Props 类型定义
  index.tsx                # barrel 导出
  __tests__/
    MyComponent.test.tsx   # 测试
  MyComponent.zh-CN.md     # 必需：中文文档
```

---

## Props 设计规范

### 命名约定

统一使用以下命名，新组件不允许使用右列旧名：

| 语义 | 标准命名 | 类型 | 禁止使用 |
|---|---|---|---|
| 开关/选中状态 | `checked` | `boolean` | ~~`value`~~（boolean 语义时） |
| 开关/选中回调 | `onChange` | `(val: boolean) => void` | ~~`onValueChange`~~ |
| 值变化回调 | `onChange` | `(val: T) => void` | ~~`onValueChange`~~ |
| 点击回调 | `onPress` | `() => void` | ~~`onClick`~~ |
| 长按回调 | `onLongPress` | `() => void` | - |
| 禁用状态 | `disabled` | `boolean` | ~~`enable`~~（避免反向布尔） |
| 左侧图标 | `leadingIcon` | `ReactElement<SvgProps>` | ~~`leftIcon`~~, ~~`leftIconSource`~~, ~~`icon`~~（歧义时） |
| 小红点 | `badge` | `boolean` | ~~`showDot`~~, ~~`dot`~~ |
| 右侧文案 | `value` | `string` | ~~`valueText`~~, ~~`text`~~ |
| 标题 | `title` | `string` | - |
| 副标题 | `subtitle` | `string \| ReactElement` | ~~`subTitle`~~（驼峰一致性） |
| 标题状态内容 | `titleStatus` | `string \| ReactNode` | ~~`statusRender`~~ |
| 自定义渲染 | `customRender` | `ReactElement` | ~~`customRenderer`~~ |
| 色系 | `colorType` | `ColorType` | ~~`color`~~, ~~`theme`~~ |
| 是否可见 | `visible` | `boolean` | ~~`show`~~, ~~`isVisible`~~ |
| 关闭回调 | `onDismiss` | `() => void` | ~~`onClose`~~, ~~`onHide`~~ |

### 回调命名规则

| 场景 | 命名模式 | 示例 |
|---|---|---|
| 用户交互触发 | `on` + 动词 | `onPress`, `onChange`, `onDismiss`, `onSelect` |
| 状态变化通知 | `on` + 名词 + 动词 | `onModalShow`, `onModalHide`, `onCheckboxChange` |
| 值变化 | `onChange` | 统一用 `onChange`，不用 `onValueChange` |

**规则**：
- 回调参数名始终以 `on` 开头
- 布尔状态变化用 `onChange`，参数为新值 `(val: boolean) => void`
- 非布尔值变化也用 `onChange`，参数为新值 `(val: T) => void`
- 点击事件用 `onPress`（RN 惯例），不用 `onClick`（Web 惯例）

### 控制型 Props 规则

| 场景 | 规则 | 示例 |
|---|---|---|
| 箭头/导航图标 | 由 `onPress` 控制，有 `onPress` 则显示 | `const showArrow = !!onPress;` |
| 右侧操作区 | 用 `actionType` 字符串联合类型 | `'navigate' \| 'switch' \| 'none' \| 'custom'` |
| 操作类型图标 | `iconType` 字符串联合类型 | `'plusMinus' \| 'leftRight'` |
| 符号类型 | `symbolType` 字符串联合类型 | `'percent' \| 'degree' \| 'custom'` |

**不使用单独的 show/hide 布尔参数**控制 UI 元素显隐，优先通过其他参数隐式推断：

```tsx
// 推荐：箭头由 onPress 隐式控制
const showArrow = !!onPress;

// 不推荐：多余的布尔参数
interface Props {
  showArrow?: boolean;    // 多余
  hideRightIcon?: boolean; // 多余且语义反转
}
```

### 图标 Props 类型约束

| 场景 | 类型 | 说明 |
|---|---|---|
| SVG 图标 | `ReactElement<SvgProps>` | 精确约束为 SVG 组件 |
| SVG 或 Image | `ReactElement<SvgProps> \| ReactElement<{ source: ImageSourcePropType }>` | 同时接受两种 |
| 任意渲染内容 | `ReactNode` | 仅在确实需要文本/片段等非组件内容时使用 |
| 自定义组件渲染 | `ReactElement` | 不用 `ReactNode`，排除 string/number/null |

---

## 类型定义规范

### 默认不使用 enum

使用 `as const` 数组 + 类型推导：

```tsx
// 推荐
export const ACTION_TYPES = ['navigate', 'switch', 'none', 'custom'] as const;
export type ActionType = (typeof ACTION_TYPES)[number];

// 不推荐
export enum ActionType { NAVIGATE = 'navigate', ... }
```

**原因**：enum 编译后生成额外代码，且不支持 `Array.includes()` 等运行时操作。

HyperOS 当前存在 `EmptyImageType` 这一兼容性例外（`src/hyperOS/components/emptyState/interface.ts`）。新增类型仍按上述 `as const` 方案实现，除非已有公开 API 需要保持兼容。

### interface 组织

- Props 接口放在 `interface.ts`，不内联在组件文件中
- 用 `extends` 复用基础类型，不重复声明字段
- 类型别名用 `type`，数据结构用 `interface`

```tsx
// interface.ts
export interface BaseProps extends AccessibilityPropsType {
  title: string;
  disabled?: boolean;
}

export interface MyComponentProps extends BaseProps {
  // 只声明新增字段
  checked?: boolean;
  onChange: (val: boolean) => void;
}
```

### 纯透传 Props 用类型别名

如果子组件 Props 完全等于父类型，用 `type` 而非重新定义 `interface`：

```tsx
export type ActionCustomDialogPropsType = BaseDialogPropsType;
```

---

## 组件实现规范

### referenceReport

每个组件必须在 `useEffect` 中调用一次：

```tsx
useEffect(() => {
  referenceReport('ComponentName');
}, []);
```

### 颜色使用

- 通过 `ConfigContext` 获取 `colorToken`，不硬编码颜色值
- IoT 组件色系变体通过 `useColorTypeState` Hook

```tsx
const { colorToken } = useContext(ConfigContext);
const primaryColor = useColorTypeState({ colorType, disabled });
```

### 样式规范

- 使用 `StyleSheet.create` 定义静态样式
- **不使用 `gap` 属性**（RN 0.61 不支持），用负 margin 容器 + 子项 margin 模拟间距
- 使用 `Fonts` Token，不手写 `fontSize` / `fontWeight`
- Prettier：140 字符宽、单引号、尾逗号 es5、2 空格缩进

### 间距模拟（替代 gap）

```tsx
// 容器负 margin 抵消子项外边距
container: { flexDirection: 'row', flexWrap: 'wrap', marginHorizontal: -4, marginVertical: -4 }

// 子项正 margin 产生间距（相邻元素间隔 = 4 + 4 = 8px）
item: { flexBasis: '50%', flexShrink: 1, flexGrow: 1, marginHorizontal: 4, marginVertical: 4 }
```

### flex 布局等分

不使用 `onLayout` + `PixelRatio` 动态计算宽度，使用 flex 方案：

```tsx
// 容器计算 flexBasis 百分比传给子项
const flexBasis = `${Math.floor(10000 / childrenCount) / 100}%`;
```

### 无障碍

使用 `getAccessibilityConfig()` 统一处理：

```tsx
import { AccessibilityRoles, getAccessibilityConfig } from '@utils/accessibility-helper';

<View {...getAccessibilityConfig({
  accessible,
  accessibilityRole: AccessibilityRoles.button,
  accessibilityLabel,
  accessibilityHint,
})} />
```

---

## 测试规范

- 测试目录：`__tests__/`（复数）
- 测试框架：`@testing-library/react-native`
- 文件后缀：`.test.tsx`
- 运行单个测试：`npx jest path/to/file.test.tsx`
- 运行全部测试：`npm test`

### 测试要点

- 默认渲染：组件使用默认 props 正常渲染
- Props 变体：每种 `actionType` / `iconType` 等变体至少一个用例
- 交互：`onPress`、`onChange` 回调正确触发
- 禁用态：`disabled` 时回调不触发
- 边界：子元素数量限制、空值处理

---

## 导出检查清单

新增或重命名组件后，确保以下文件同步更新：

- [ ] `src/hyperOS/components/xxx/index.tsx` — 组件 barrel 导出
- [ ] `src/hyperOS/index.ts` — 公共 API 导出
- [ ] `importMap.js` — babel-plugin-import 路径映射
- [ ] `hyperOSUI/index.js`（消费方 SDK）— re-export
- [ ] `docs/components.md` — API 文档
- [ ] Demo 文件（消费方项目）

---

## Git 规范

- 分支：`master`、`feature_*`、`release_*`、`hotfix_*`
- 提交：使用 `npm run commit`（commitizen）
- CI：GitLab CI，阶段为 install → eslint → publish
- 如果当前构建命中版本号 tag（如 `v1.18.16`），发包/发布流程可以跳过 ai-review，避免整体耗时过长

---

## AI 排查速查

| 问题 | 关键词/入口 |
|------|------------|
| 新组件在哪创建 | `src/hyperOS/components/`（基础）或 `src/hyperOS/IotComponents/`（IoT） |
| Props 命名不确定 | 见上方 Props 设计规范表 |
| 导出后消费方找不到 | 检查 5 处同步：index.ts、importMap.js、hyperOSUI、docs、demo |
| 测试报错 setup | 检查 `jestSetup.js` mock 和 `tsconfig.test.json` |
| enum 报错 | 新增类型优先改用 `as const` 数组 + 类型推导；`EmptyImageType` 是现有兼容性例外 |
| 样式 gap 不生效 | 用负 margin 容器 + 子项正 margin 模拟 |
