关于 data-to-ui
data-to-ui 是面向开发者的技能,帮助将静态数据转为类型安全的 React 界面,涵盖 JSON、TypeScript 到 React 的流程,以及语义映射、格式化和聚合。
主要功能
- 01将 JSON 结构转为 TypeScript 类型
- 02为 UI 组件创建派生类型
- 03语义化颜色与图标映射
- 04数值和日期格式化工具
- 05类型安全的分组与汇总
适用场景
- 01根据 JSON 数据构建 React 组件
- 02统一应用严重程度颜色和图标
- 03格式化显示数值与日期
- 04分组记录并计算汇总数据
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dralgorhythm/ claude-agentic-framework --skill data-to-ui安装后在 Claude Code 中输入/data-to-ui确认已加载。
详细介绍
数据到 UI
概述
将静态数据转换为类型安全的 React 组件的模式。本技能涵盖 JSON → TypeScript → React 流程,重点介绍语义化颜色系统、派生类型和格式化工具。
工作流
1. JSON Schema → TypeScript 类型
- 读取 JSON schema/数据结构
- 创建与 JSON 结构匹配的基础 TypeScript 接口
- 导出枚举的联合类型(例如,
type Severity = 'safety_hazard' | 'repair_needed') - 对可为 null 或缺失的字段使用可选属性(
?) - 为复杂类型添加 JSDoc 注释
2. UI 派生类型
- 创建通过
extends扩展基础类型的组合类型 - 使用
Pick<T, K>和Omit<T, K>定义组件属性 - 使用
&构建用于合并数据的交叉类型(例如,FindingWithAsset) - 为统计数据/摘要创建聚合接口
3. 颜色映射系统
- 定义
Record<EnumType, ColorValue>,用于映射语义化颜色 - 提供多种颜色格式:徽章、背景、文本、边框、圆点
- 使用 Tailwind 实用类(例如,
'bg-red-500 text-red-600') - 导出访问器函数(例如,
getSeverityColors()) - 使用注释说明颜色选择
4. 图标映射
- 创建
Record<EnumType, string>,将类型映射到 lucide-react 图标名称 - 使用 PascalCase 图标名称(例如,
'AlertTriangle'、'Thermometer') - 导出访问器函数(例如,
getSeverityIcon())
5. 格式化工具
- 货币:使用
Intl.NumberFormat,格式为美元且不显示小数 - 日期:使用
toLocaleDateString,并采用简短月份格式 - 计算:创建用于计算年数、百分比和使用寿命的辅助函数
- 标签:创建易于理解的标签映射
6. 聚合与分组
- 使用 reduce 或 forEach 实现
groupBy模式 - 使用自定义比较器,按严重程度/优先级排序
- 计算摘要统计数据(最小值、最大值、平均值、计数)
- 返回强类型聚合结果
参考实现
颜色映射系统
// Single source of truth for semantic colors
export interface SeverityColors {
badge: string; // 'text-red-600 bg-red-100'
bg: string; // 'bg-red-500'
text: string; // 'text-red-600'
border: string; // 'border-red-500'
dot: string; // 'bg-red-500'
}
const SEVERITY_COLOR_MAP: Record<Severity, SeverityColors> = {
safety_hazard: {
badge: 'text-red-600 bg-red-100',
bg: 'bg-red-500',
text: 'text-red-600',
dot: 'bg-red-500',
border: 'border-red-500',
},
// ... other severities
};
export function getSeverityColors(severity: Severity): SeverityColors {
return SEVERITY_COLOR_MAP[severity];
}
图标映射
export function getSeverityIcon(severity: Severity): string {
const icons: Record<Severity, string> = {
safety_hazard: 'AlertTriangle',
repair_needed: 'Wrench',
maintenance_item: 'Settings',
monitor: 'Eye',
informational: 'Info'
};
return icons[severity];
}
派生类型
// Base type
export interface Finding {
id: string;
assetId?: string | null;
severity: Severity;
title: string;
}
// Derived type with relationship
export interface FindingWithAsset extends Finding {
asset?: Asset;
}
// Aggregate type
export interface PropertyWithDetails {
property: Property;
inspectionReport: InspectionReport;
findings: Finding[];
assets: Asset[];
}
格式化工具
export function formatCurrency(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(value);
}
export function formatDate(dateString: string): string {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
export function yearsSince(dateString: string): number {
const date = new Date(dateString);
const now = new Date();
return Math.floor((now.getTime() - date.getTime()) / (365.25 * 24 * 60 * 60 * 1000));
}
聚合模式
// Group by enum value
export function groupFindingsBySeverity(findings: Finding[]): Record<Severity, Finding[]> {
const grouped: Record<Severity, Finding[]> = {
safety_hazard: [],
repair_needed: [],
maintenance_item: [],
monitor: [],
informational: []
};
findings.forEach(f => grouped[f.severity].push(f));
return grouped;
}
// Sort by priority
export function sortFindingsBySeverity(findings: Finding[]): Finding[] {
const severityOrder: Record<Severity, number> = {
safety_hazard: 0,
repair_needed: 1,
maintenance_item: 2,
monitor: 3,
informational: 4
};
return [...findings].sort((a, b) =>
severityOrder[a.severity] - severityOrder[b.severity]
);
}
最佳实践
- 单一事实来源:将所有颜色/图标映射集中在一处,并提供访问器函数
- 多种颜色格式:提供徽章、背景、文本、边框和圆点等变体,提升灵活性
- 类型安全:使用
Record<EnumType, Value>,而非普通对象 - Intl API:使用
Intl.NumberFormat和Intl.DateTimeFormat实现本地化 - 不可变性:对数组进行排序/筛选时使用展开运算符
- 文档:添加 JSDoc 注释,说明颜色选择和数据结构
- 工具就近放置:将类型和工具放在同一文件中,以便导入
反模式
- 不要使用没有语义含义的通用颜色名称(例如,使用
'safety_hazard',而不是'red') - 不要在组件中内联颜色类;始终使用映射函数
- 不要使用
any类型;优先使用unknown和类型守卫 - 不要在排序/筛选函数中修改输入数组;始终创建副本
- 不要硬编码日期格式;使用
Intl.DateTimeFormat保持一致 - 不要为简单工具创建单独文件;应与类型放在一起
- 不要忘记处理可选字段中的 null/undefined
- 不要在 TypeScript 文件名中使用 snake_case 或 kebab-case;应使用 camelCase
反馈循环
- 类型检查:运行
tsc --noEmit验证类型 - 运行时验证:考虑使用 Zod 在运行时验证 JSON schema
- 视觉测试:构建 Storybook stories,验证颜色系统
- 数据一致性:将聚合统计数据与源数据计数进行比较
- 导入验证:确保所有工具都已导出且可以导入
相关技能
interface-design- 在 React 组件中使用颜色系统refactoring-code- 整合重复的颜色/格式化逻辑
评论
支持 Markdown