@lark-apaas/coding-templates 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/package.json +1 -1
  2. package/template-vite-react/README.md +175 -0
  3. package/template-vite-react/client/index.html +2 -1
  4. package/template-vite-react/client/src/components/layout.tsx +0 -2
  5. package/template-vite-react/client/src/components/ui/README.md +134 -0
  6. package/template-vite-react/client/src/components/ui/accordion.tsx +22 -28
  7. package/template-vite-react/client/src/components/ui/alert-dialog.tsx +34 -64
  8. package/template-vite-react/client/src/components/ui/alert.tsx +10 -15
  9. package/template-vite-react/client/src/components/ui/aspect-ratio.tsx +5 -16
  10. package/template-vite-react/client/src/components/ui/avatar.tsx +11 -67
  11. package/template-vite-react/client/src/components/ui/badge.tsx +21 -31
  12. package/template-vite-react/client/src/components/ui/breadcrumb.tsx +23 -39
  13. package/template-vite-react/client/src/components/ui/button.tsx +36 -25
  14. package/template-vite-react/client/src/components/ui/calendar.tsx +37 -43
  15. package/template-vite-react/client/src/components/ui/card.tsx +73 -94
  16. package/template-vite-react/client/src/components/ui/carousel.tsx +7 -8
  17. package/template-vite-react/client/src/components/ui/chart.tsx +35 -49
  18. package/template-vite-react/client/src/components/ui/checkbox.tsx +10 -7
  19. package/template-vite-react/client/src/components/ui/collapsible.tsx +20 -6
  20. package/template-vite-react/client/src/components/ui/command.tsx +52 -40
  21. package/template-vite-react/client/src/components/ui/context-menu.tsx +170 -117
  22. package/template-vite-react/client/src/components/ui/dialog.tsx +37 -52
  23. package/template-vite-react/client/src/components/ui/drawer.tsx +12 -9
  24. package/template-vite-react/client/src/components/ui/dropdown-menu.tsx +194 -133
  25. package/template-vite-react/client/src/components/ui/hover-card.tsx +24 -29
  26. package/template-vite-react/client/src/components/ui/input-group.tsx +39 -29
  27. package/template-vite-react/client/src/components/ui/input-otp.tsx +7 -17
  28. package/template-vite-react/client/src/components/ui/input.tsx +4 -3
  29. package/template-vite-react/client/src/components/ui/label.tsx +9 -3
  30. package/template-vite-react/client/src/components/ui/menubar.tsx +160 -92
  31. package/template-vite-react/client/src/components/ui/navigation-menu.tsx +45 -45
  32. package/template-vite-react/client/src/components/ui/pagination.tsx +32 -35
  33. package/template-vite-react/client/src/components/ui/popover.tsx +20 -62
  34. package/template-vite-react/client/src/components/ui/progress.tsx +14 -64
  35. package/template-vite-react/client/src/components/ui/radio-group.tsx +20 -13
  36. package/template-vite-react/client/src/components/ui/resizable.tsx +18 -10
  37. package/template-vite-react/client/src/components/ui/scroll-area.tsx +13 -10
  38. package/template-vite-react/client/src/components/ui/select.tsx +122 -78
  39. package/template-vite-react/client/src/components/ui/separator.tsx +7 -4
  40. package/template-vite-react/client/src/components/ui/sheet.tsx +42 -41
  41. package/template-vite-react/client/src/components/ui/sidebar.tsx +162 -156
  42. package/template-vite-react/client/src/components/ui/skeleton.tsx +1 -1
  43. package/template-vite-react/client/src/components/ui/slider.tsx +52 -22
  44. package/template-vite-react/client/src/components/ui/sonner.tsx +44 -26
  45. package/template-vite-react/client/src/components/ui/switch.tsx +9 -8
  46. package/template-vite-react/client/src/components/ui/table.tsx +5 -5
  47. package/template-vite-react/client/src/components/ui/tabs.tsx +24 -38
  48. package/template-vite-react/client/src/components/ui/textarea.tsx +1 -1
  49. package/template-vite-react/client/src/components/ui/toggle-group.tsx +14 -20
  50. package/template-vite-react/client/src/components/ui/toggle.tsx +13 -10
  51. package/template-vite-react/client/src/components/ui/tooltip.tsx +30 -33
  52. package/template-vite-react/client/src/index.css +130 -0
  53. package/template-vite-react/client/src/main.tsx +1 -4
  54. package/template-vite-react/components.json +2 -6
  55. package/template-vite-react/eslint.config.js +11 -0
  56. package/template-vite-react/package.json +27 -2
  57. package/template-vite-react/client/src/components/header.tsx +0 -22
  58. package/template-vite-react/client/src/components/theme-provider.tsx +0 -45
  59. package/template-vite-react/client/src/components/ui/icons/file-ae-colorful-icon.tsx +0 -21
  60. package/template-vite-react/client/src/components/ui/icons/file-ai-colorful-icon.tsx +0 -36
  61. package/template-vite-react/client/src/components/ui/icons/file-android-colorful-icon.tsx +0 -33
  62. package/template-vite-react/client/src/components/ui/icons/file-audio-colorful-icon.tsx +0 -21
  63. package/template-vite-react/client/src/components/ui/icons/file-code-colorful-icon.tsx +0 -28
  64. package/template-vite-react/client/src/components/ui/icons/file-csv-colorful-icon.tsx +0 -21
  65. package/template-vite-react/client/src/components/ui/icons/file-eml-colorful-icon.tsx +0 -29
  66. package/template-vite-react/client/src/components/ui/icons/file-ios-colorful-icon.tsx +0 -25
  67. package/template-vite-react/client/src/components/ui/icons/file-keynote-colorful-icon.tsx +0 -29
  68. package/template-vite-react/client/src/components/ui/icons/file-pages-colorful-icon.tsx +0 -29
  69. package/template-vite-react/client/src/components/ui/icons/file-ps-colorful-icon.tsx +0 -21
  70. package/template-vite-react/client/src/components/ui/icons/file-sketch-colorful-icon.tsx +0 -21
  71. package/template-vite-react/client/src/components/ui/icons/file-slide-colorful-icon.tsx +0 -21
  72. package/template-vite-react/client/src/components/ui/icons/file-vcf-colorful-icon.tsx +0 -29
  73. package/template-vite-react/client/src/components/ui/icons/file-wiki-excel-colorful-icon.tsx +0 -23
  74. package/template-vite-react/client/src/components/ui/icons/file-wiki-image-colorful-icon.tsx +0 -27
  75. package/template-vite-react/client/src/components/ui/icons/file-wiki-pdf-colorful-icon.tsx +0 -20
  76. package/template-vite-react/client/src/components/ui/icons/file-wiki-ppt-colorful-icon.tsx +0 -21
  77. package/template-vite-react/client/src/components/ui/icons/file-wiki-text-colorful-icon.tsx +0 -12
  78. package/template-vite-react/client/src/components/ui/icons/file-wiki-unknown-colorful-icon.tsx +0 -14
  79. package/template-vite-react/client/src/components/ui/icons/file-wiki-video-colorful-icon.tsx +0 -23
  80. package/template-vite-react/client/src/components/ui/icons/file-wiki-word-colorful-icon.tsx +0 -38
  81. package/template-vite-react/client/src/components/ui/icons/file-wiki-zip-colorful-icon.tsx +0 -21
  82. package/template-vite-react/client/src/types/index.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-templates",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "OpenClaw project templates for mclaw CLI",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,175 @@
1
+ # 开发规范
2
+
3
+ ## 技术栈
4
+
5
+ - 前端: React 19 + TypeScript
6
+ - 样式: Tailwind CSS v4
7
+ - UI 组件: shadcn/ui `import { Button } from "@/components/ui/button";`
8
+ - 图标: lucide-react `import { SearchIcon } from "lucide-react";`
9
+ - 图表: ReactECharts `import ReactECharts from "echarts-for-react";`
10
+ - 动画: framer-motion `import { motion } from "framer-motion";`
11
+ - 路由: react-router-dom `import { Link, useNavigate } from "react-router-dom";`
12
+
13
+ ---
14
+
15
+ ## 项目结构
16
+
17
+ ```
18
+ client/src/
19
+ ├── app.tsx # 路由注册
20
+ ├── index.css # 全局样式 + 主题变量
21
+ ├── components/ # 全局共享组件
22
+ │ └── ui/ # shadcn/ui 内置组件(勿手动修改)
23
+ ├── pages/ # 页面模块(每个页面一个目录)
24
+ │ └── home/
25
+ │ ├── index.tsx # 页面入口
26
+ │ └── components/ # 页面专属组件
27
+ ├── hooks/ # 自定义 Hooks
28
+ └── lib/ # 工具函数(cn() 等)
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 页面与组件规范
34
+
35
+ **页面文件只做骨架编排,不包含具体 UI 实现。**
36
+
37
+ ```tsx
38
+ // client/src/pages/dashboard/index.tsx
39
+ import { StatsSection } from "./components/stats-section";
40
+ import { DataTableSection } from "./components/data-table-section";
41
+
42
+ export default function DashboardPage() {
43
+ return (
44
+ <div className="space-y-8">
45
+ <StatsSection />
46
+ <DataTableSection />
47
+ </div>
48
+ );
49
+ }
50
+ ```
51
+
52
+ **规则:**
53
+
54
+ - 每个视觉上独立的区块拆为一个组件文件,即使只出现一次
55
+ - 单个组件文件不超过 **150 行**,超出时进一步拆分子组件
56
+ - 页面专属组件放在 `pages/<page>/components/`
57
+ - 跨页面复用的组件放在 `client/src/components/`
58
+ - 相同 UI 片段出现 **≥2 次**时,必须提取为可复用组件
59
+ - 文件名 kebab-case(`stat-card.tsx`),组件名 PascalCase(`StatCard`)
60
+ - 组件之间**禁止循环引用**
61
+
62
+ ---
63
+
64
+ ## 路由注册
65
+
66
+ 新增页面在 `client/src/app.tsx` 中注册:
67
+
68
+ ```tsx
69
+ <Route element={<Layout />}>
70
+ <Route index element={<HomePage />} />
71
+ <Route path="dashboard" element={<DashboardPage />} />
72
+ <Route path="*" element={<NotFoundPage />} />
73
+ </Route>
74
+ ```
75
+
76
+ **新增页面步骤:**
77
+
78
+ 1. 在 `client/src/pages/` 下新建页面目录和 `index.tsx`
79
+ 2. 在 `app.tsx` 的 `<Routes>` 内添加 `<Route>` 配置
80
+
81
+ **路由跳转必须使用 react-router-dom:**
82
+
83
+ - 组件内跳转:`<Link to="/dashboard">Dashboard</Link>`
84
+ - 编程式跳转:`const navigate = useNavigate(); navigate("/dashboard");`
85
+ - **禁止使用** `<a href="/">` 或 `window.location` 进行页面内跳转
86
+
87
+ ---
88
+
89
+ ## 样式与主题
90
+
91
+ ### 主题变量
92
+
93
+ 主题色定义在 `client/src/index.css` 中,通过 `:root` CSS 变量 + `@theme inline` 注册到 Tailwind。
94
+
95
+ **语义化颜色对照:**
96
+
97
+ | 用途 | Tailwind 类 | CSS 变量 |
98
+ |------|------------|----------|
99
+ | 页面背景 | `bg-background` | `--background` |
100
+ | 主文本 | `text-foreground` | `--foreground` |
101
+ | 卡片背景 | `bg-card` | `--card` |
102
+ | 次要文本 | `text-muted-foreground` | `--muted-foreground` |
103
+ | 主色 | `bg-primary` / `text-primary` | `--primary` |
104
+ | 强调色 | `bg-accent` | `--accent` |
105
+ | 边框 | `border-border` | `--border` |
106
+ | 危险色 | `text-destructive` | `--destructive` |
107
+ | 成功色 | `text-success-foreground` | `--success-foreground` |
108
+ | 警告色 | `text-warning-foreground` | `--warning-foreground` |
109
+ | 图表色 | `bg-chart-1` ~ `bg-chart-5` | `--chart-1` ~ `--chart-5` |
110
+
111
+ **颜色使用规则:**
112
+
113
+ - 主题色(背景、文本、主色、边框等)**必须使用语义化变量类**
114
+ - 灰阶辅助色(细节装饰、次要分隔线)可使用 Tailwind 原生色(如 `text-gray-500`)
115
+ - 类名合并使用 `cn()`:`import { cn } from "@/lib/utils"`
116
+
117
+ ### 主题增量修改规范
118
+
119
+ 修改主题时,**只覆盖需要变更的变量**:
120
+
121
+ ```css
122
+ /* 正确:仅修改需要的变量 */
123
+ :root {
124
+ --primary: hsl(150, 60%, 40%);
125
+ --primary-foreground: hsl(0, 0%, 100%);
126
+ }
127
+
128
+ /* 禁止:复制整个 :root 块后修改 */
129
+ ```
130
+
131
+ - 新增自定义颜色变量时,必须同时在 `:root` 和 `@theme inline` 中注册
132
+ - 禁止直接修改 `@theme inline` 中已有的 `--color-*` 映射关系
133
+ - 禁止删除已有的主题变量(可能被 shadcn/ui 组件依赖)
134
+
135
+ ---
136
+
137
+ ## 布局与交互
138
+
139
+ ### 响应式布局
140
+
141
+ - 容器使用 `max-w-*` + `mx-auto` 居中,禁止内容在大屏贴边延伸
142
+ - 多列布局使用 `grid` + 断点类:`grid-cols-1 md:grid-cols-2 lg:grid-cols-3`
143
+ - flex 子元素设置 `min-w-0`,多元素横排时加 `flex-wrap`
144
+ - 禁止固定像素宽度作为主容器(如 `w-[720px]`)
145
+
146
+ ### 内容自适应
147
+
148
+ - 区块高度由内容撑开,禁止固定 `h-` 值(图表容器除外)
149
+ - 图片:`max-w-full h-auto`
150
+ - 长文本:`break-words`
151
+ - 单行截断:`truncate`
152
+ - 表格/代码块:`overflow-x-auto`
153
+
154
+ ### 交互规范
155
+
156
+ - 所有交互元素(按钮、链接、标签页等)必须有**实际交互逻辑**和**可见反馈**
157
+ - 禁止空函数(`onClick={() => {}}`)或仅 `console.log` 的响应
158
+ - 禁止 `href="#"` 占位链接、无内容切换的标签页、空下拉菜单
159
+ - 禁止"导出"、"分享"等无法真正执行的操作按钮
160
+ - 如功能暂未实现,**删除该入口**,不实现假按钮
161
+
162
+ ---
163
+
164
+ ## 自检清单
165
+
166
+ | 检查项 | 验收标准 |
167
+ |--------|---------|
168
+ | 页面拆分 | 页面文件只做骨架编排;每个区块为独立组件;单文件 ≤150 行 |
169
+ | 组件复用 | 相同片段 ≥2 次已提取为组件;文件名 kebab-case,组件名 PascalCase |
170
+ | 路由注册 | 新页面已在 `app.tsx` 注册;跳转使用 `<Link>` / `useNavigate()`,无 `<a href>` |
171
+ | 主题色 | 使用语义化变量类(`bg-background`、`text-primary` 等);未硬编码颜色值 |
172
+ | 主题修改 | 仅增量覆盖变更的变量;新增色同时注册 `:root` 和 `@theme inline` |
173
+ | 响应式 | 容器 `max-w-*` + `mx-auto`;多列布局窄屏退化单列;flex 有 `min-w-0` |
174
+ | 内容自适应 | 无固定 `h-`(图表除外);长文本 `break-words`;表格 `overflow-x-auto` |
175
+ | 交互完整性 | 所有按钮/链接有实际处理器和可见反馈;无空响应、无假按钮 |
@@ -4,7 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>{{projectName}}</title>
7
+ <title>应用标题</title>
8
+ <link href="https://lf3-static.bytednsdoc.com/obj/eden-cn/ylcylz_fsph_ryhs/ljhwZthlaukjlkulzlp/feisuda/feisuda.svg" rel="shortcut icon"/>
8
9
  </head>
9
10
  <body>
10
11
  <div id="root"></div>
@@ -1,10 +1,8 @@
1
1
  import { Outlet } from "react-router-dom";
2
- import { Header } from "@/components/header";
3
2
 
4
3
  export function Layout() {
5
4
  return (
6
5
  <div className="min-h-screen bg-background text-foreground">
7
- <Header />
8
6
  <main className="container mx-auto px-4 py-8">
9
7
  <Outlet />
10
8
  </main>
@@ -0,0 +1,134 @@
1
+ # shadcn/ui 组件开发指南
2
+
3
+ ## 核心原则
4
+
5
+ - 组件位置:`/client/src/components/ui/`
6
+ - 图标库:必须使用 `lucide-react`,禁用 Emoji
7
+ - 查阅源码实现:直接读取组件源码了解最新组件实现
8
+ - 交互系统:Button、Badge 使用 elevate 遮罩系统处理 hover/active 状态(通过 `::after` 伪元素叠加 `--elevate-1` / `--elevate-2`)
9
+
10
+ ## 关键组件规范
11
+
12
+ #### 1. Button 组件
13
+
14
+ **导入路径**:`import { Button } from '@/components/ui/button'`
15
+
16
+ **Props**:
17
+ - `variant`: `"link" | "default" | "destructive" | "outline" | "secondary" | "ghost"` - 按钮样式变体
18
+
19
+ ```tsx
20
+ // ✅ 使用变体
21
+ <Button variant="secondary">标准按钮</Button>
22
+
23
+ // ✅ 自定义颜色时必须配对前景色
24
+ <Button className="bg-primary text-primary-foreground">自定义按钮</Button>
25
+ ```
26
+
27
+ #### 2. Badge 组件
28
+
29
+ **导入路径**:`import { Badge } from '@/components/ui/badge'`
30
+
31
+ **Props**:
32
+ - `variant`: `"default" | "destructive" | "outline" | "secondary"` - 样式变体
33
+
34
+ ```tsx
35
+ // ✅ 使用变体
36
+ <Badge>默认</Badge>
37
+ <Badge variant="secondary">次要</Badge>
38
+ <Badge variant="outline">边框</Badge>
39
+ <Badge variant="destructive">危险</Badge>
40
+ ```
41
+
42
+ #### 3. Alert 组件
43
+
44
+ **导入路径**:`import { Alert, AlertTitle, AlertDescription, AlertAction } from '@/components/ui/alert'`
45
+
46
+ **Props**:
47
+ - `variant`: `"default" | "destructive" | "success" | "warning"` - 样式变体
48
+
49
+ ```tsx
50
+ // ✅ default / destructive / success / warning 四种变体
51
+ <Alert variant="success">
52
+ <CheckCircle className="size-4" />
53
+ <AlertTitle>操作成功</AlertTitle>
54
+ <AlertDescription>您的更改已保存</AlertDescription>
55
+ </Alert>
56
+
57
+ <Alert variant="warning">
58
+ <AlertTriangle className="size-4" />
59
+ <AlertTitle>警告</AlertTitle>
60
+ <AlertDescription>请注意检查</AlertDescription>
61
+ </Alert>
62
+
63
+ ```
64
+
65
+ #### 4. Empty 组件
66
+
67
+ **导入路径**:`import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent } from '@/components/ui/empty'`
68
+
69
+ **子组件 Props**:
70
+ - `EmptyMedia` 组件的 `variant`: `"default" | "icon"` - 媒体展示方式
71
+
72
+ ```tsx
73
+ // 标准结构:EmptyHeader 包含 EmptyMedia + EmptyTitle + EmptyDescription
74
+ <Empty>
75
+ <EmptyHeader>
76
+ <EmptyMedia variant="icon">
77
+ <SearchIcon className="size-6" />
78
+ </EmptyMedia>
79
+ <EmptyTitle>暂无数据</EmptyTitle>
80
+ <EmptyDescription>当前没有找到相关内容</EmptyDescription>
81
+ </EmptyHeader>
82
+ <EmptyContent>
83
+ <Button>添加数据</Button>
84
+ </EmptyContent>
85
+ </Empty>
86
+ ```
87
+
88
+ #### 5. Card Padding 系统
89
+
90
+ **导入路径**:`import { CardHeader, CardContent, CardFooter } from '@/components/ui/card'`
91
+
92
+ - `CardHeader`: `p-6` (24px 全方向)
93
+ - `CardContent`: `p-6 pt-0` (与 header 无缝衔接)
94
+ - `CardFooter`: `p-6 pt-0` (与 content 无缝衔接)
95
+
96
+ #### 6. Dialog 组件
97
+
98
+ **导入路径**:`import { Dialog, DialogContent } from '@/components/ui/dialog'`
99
+
100
+ Dialog 默认提供了右上角的close能力,同时也提供了自定义关闭按钮的能力,即通过设置showCloseButton为false来关闭默认的关闭按钮。所以当默认存在close时,禁止在内容区域提供自定义的关闭按钮。
101
+
102
+ #### 7. Image 组件
103
+
104
+ **导入路径**:`import { Image } from '@/components/ui/image'`
105
+
106
+ **Props**:支持原生 `<img>` 标签所有属性。
107
+
108
+ ```typescript
109
+ interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement>
110
+ ```
111
+
112
+ **使用规范**:
113
+
114
+ 1. **响应式图片场景**:当图片尺寸需要根据视口宽度变化时,必须设置 `sizes` 属性
115
+ 2. **固定尺寸图片场景**:当图片有固定尺寸时,必须设置 `width` 属性(number 类型)
116
+ 3. **必须提供有意义的 `alt` 属性**
117
+
118
+ ```tsx
119
+ // ✅ 响应式图片
120
+ <Image
121
+ src="/path/to/image.jpg"
122
+ alt="描述文字"
123
+ sizes="(max-width: 768px) 100vw, 50vw"
124
+ />
125
+
126
+ // ✅ 固定尺寸图片(width/height 使用 number)
127
+ <Image
128
+ src="/path/to/image.jpg"
129
+ alt="描述文字"
130
+ width={300}
131
+ height={200}
132
+ />
133
+
134
+ ```
@@ -1,23 +1,25 @@
1
- import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
+ import { ChevronDownIcon } from "lucide-react"
2
6
 
3
7
  import { cn } from "@/lib/utils"
4
- import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
5
8
 
6
- function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
7
- return (
8
- <AccordionPrimitive.Root
9
- data-slot="accordion"
10
- className={cn("flex w-full flex-col", className)}
11
- {...props}
12
- />
13
- )
9
+ function Accordion({
10
+ ...props
11
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />
14
13
  }
15
14
 
16
- function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
15
+ function AccordionItem({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
17
19
  return (
18
20
  <AccordionPrimitive.Item
19
21
  data-slot="accordion-item"
20
- className={cn("not-last:border-b", className)}
22
+ className={cn("border-b last:border-b-0", className)}
21
23
  {...props}
22
24
  />
23
25
  )
@@ -27,20 +29,19 @@ function AccordionTrigger({
27
29
  className,
28
30
  children,
29
31
  ...props
30
- }: AccordionPrimitive.Trigger.Props) {
32
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
31
33
  return (
32
34
  <AccordionPrimitive.Header className="flex">
33
35
  <AccordionPrimitive.Trigger
34
36
  data-slot="accordion-trigger"
35
37
  className={cn(
36
- "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
38
+ "focus-visible:border-ring focus-visible:ring-ring/20 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none enabled:hover:underline focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
37
39
  className
38
40
  )}
39
41
  {...props}
40
42
  >
41
43
  {children}
42
- <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
43
- <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
44
+ <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
44
45
  </AccordionPrimitive.Trigger>
45
46
  </AccordionPrimitive.Header>
46
47
  )
@@ -50,22 +51,15 @@ function AccordionContent({
50
51
  className,
51
52
  children,
52
53
  ...props
53
- }: AccordionPrimitive.Panel.Props) {
54
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
54
55
  return (
55
- <AccordionPrimitive.Panel
56
+ <AccordionPrimitive.Content
56
57
  data-slot="accordion-content"
57
- className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
58
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
58
59
  {...props}
59
60
  >
60
- <div
61
- className={cn(
62
- "h-(--accordion-panel-height) pt-0 pb-2.5 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
63
- className
64
- )}
65
- >
66
- {children}
67
- </div>
68
- </AccordionPrimitive.Panel>
61
+ <div className={cn("pt-0 pb-4", className)}>{children}</div>
62
+ </AccordionPrimitive.Content>
69
63
  )
70
64
  }
71
65
 
@@ -1,22 +1,28 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
4
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5
5
 
6
6
  import { cn } from "@/lib/utils"
7
- import { Button } from "@/components/ui/button"
7
+ import { buttonVariants } from "@/components/ui/button"
8
8
 
9
- function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
9
+ function AlertDialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
10
12
  return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
11
13
  }
12
14
 
13
- function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
15
+ function AlertDialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
14
18
  return (
15
19
  <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
16
20
  )
17
21
  }
18
22
 
19
- function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
23
+ function AlertDialogPortal({
24
+ ...props
25
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
20
26
  return (
21
27
  <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
22
28
  )
@@ -25,12 +31,12 @@ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
25
31
  function AlertDialogOverlay({
26
32
  className,
27
33
  ...props
28
- }: AlertDialogPrimitive.Backdrop.Props) {
34
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
29
35
  return (
30
- <AlertDialogPrimitive.Backdrop
36
+ <AlertDialogPrimitive.Overlay
31
37
  data-slot="alert-dialog-overlay"
32
38
  className={cn(
33
- "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
39
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
34
40
  className
35
41
  )}
36
42
  {...props}
@@ -40,19 +46,15 @@ function AlertDialogOverlay({
40
46
 
41
47
  function AlertDialogContent({
42
48
  className,
43
- size = "default",
44
49
  ...props
45
- }: AlertDialogPrimitive.Popup.Props & {
46
- size?: "default" | "sm"
47
- }) {
50
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
48
51
  return (
49
52
  <AlertDialogPortal>
50
53
  <AlertDialogOverlay />
51
- <AlertDialogPrimitive.Popup
54
+ <AlertDialogPrimitive.Content
52
55
  data-slot="alert-dialog-content"
53
- data-size={size}
54
56
  className={cn(
55
- "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
57
+ "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-sm:max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 max-w-lg",
56
58
  className
57
59
  )}
58
60
  {...props}
@@ -68,10 +70,7 @@ function AlertDialogHeader({
68
70
  return (
69
71
  <div
70
72
  data-slot="alert-dialog-header"
71
- className={cn(
72
- "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
73
- className
74
- )}
73
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
75
74
  {...props}
76
75
  />
77
76
  )
@@ -85,23 +84,7 @@ function AlertDialogFooter({
85
84
  <div
86
85
  data-slot="alert-dialog-footer"
87
86
  className={cn(
88
- "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
89
- className
90
- )}
91
- {...props}
92
- />
93
- )
94
- }
95
-
96
- function AlertDialogMedia({
97
- className,
98
- ...props
99
- }: React.ComponentProps<"div">) {
100
- return (
101
- <div
102
- data-slot="alert-dialog-media"
103
- className={cn(
104
- "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
87
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
105
88
  className
106
89
  )}
107
90
  {...props}
@@ -116,10 +99,7 @@ function AlertDialogTitle({
116
99
  return (
117
100
  <AlertDialogPrimitive.Title
118
101
  data-slot="alert-dialog-title"
119
- className={cn(
120
- "text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
121
- className
122
- )}
102
+ className={cn("text-lg font-semibold", className)}
123
103
  {...props}
124
104
  />
125
105
  )
@@ -132,10 +112,7 @@ function AlertDialogDescription({
132
112
  return (
133
113
  <AlertDialogPrimitive.Description
134
114
  data-slot="alert-dialog-description"
135
- className={cn(
136
- "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
137
- className
138
- )}
115
+ className={cn("text-muted-foreground text-sm", className)}
139
116
  {...props}
140
117
  />
141
118
  )
@@ -144,11 +121,10 @@ function AlertDialogDescription({
144
121
  function AlertDialogAction({
145
122
  className,
146
123
  ...props
147
- }: React.ComponentProps<typeof Button>) {
124
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
148
125
  return (
149
- <Button
150
- data-slot="alert-dialog-action"
151
- className={cn(className)}
126
+ <AlertDialogPrimitive.Action
127
+ className={cn(buttonVariants(), className)}
152
128
  {...props}
153
129
  />
154
130
  )
@@ -156,16 +132,11 @@ function AlertDialogAction({
156
132
 
157
133
  function AlertDialogCancel({
158
134
  className,
159
- variant = "outline",
160
- size = "default",
161
135
  ...props
162
- }: AlertDialogPrimitive.Close.Props &
163
- Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
136
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
164
137
  return (
165
- <AlertDialogPrimitive.Close
166
- data-slot="alert-dialog-cancel"
167
- className={cn(className)}
168
- render={<Button variant={variant} size={size} />}
138
+ <AlertDialogPrimitive.Cancel
139
+ className={cn(buttonVariants({ variant: "outline" }), className)}
169
140
  {...props}
170
141
  />
171
142
  )
@@ -173,15 +144,14 @@ function AlertDialogCancel({
173
144
 
174
145
  export {
175
146
  AlertDialog,
176
- AlertDialogAction,
177
- AlertDialogCancel,
147
+ AlertDialogPortal,
148
+ AlertDialogOverlay,
149
+ AlertDialogTrigger,
178
150
  AlertDialogContent,
179
- AlertDialogDescription,
180
- AlertDialogFooter,
181
151
  AlertDialogHeader,
182
- AlertDialogMedia,
183
- AlertDialogOverlay,
184
- AlertDialogPortal,
152
+ AlertDialogFooter,
185
153
  AlertDialogTitle,
186
- AlertDialogTrigger,
154
+ AlertDialogDescription,
155
+ AlertDialogAction,
156
+ AlertDialogCancel,
187
157
  }
@@ -4,13 +4,18 @@ import { cva, type VariantProps } from "class-variance-authority"
4
4
  import { cn } from "@/lib/utils"
5
5
 
6
6
  const alertVariants = cva(
7
- "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
7
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
11
  default: "bg-card text-card-foreground",
12
12
  destructive:
13
- "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
13
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
14
+ // 新增语义化 success/warning variant
15
+ success:
16
+ "bg-success text-success-foreground border-success/50 [&>svg]:text-success-foreground *:data-[slot=alert-description]:text-success-foreground/90",
17
+ warning:
18
+ "bg-warning text-warning-foreground border-warning/50 [&>svg]:text-warning-foreground *:data-[slot=alert-description]:text-warning-foreground/90",
14
19
  },
15
20
  },
16
21
  defaultVariants: {
@@ -39,7 +44,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
39
44
  <div
40
45
  data-slot="alert-title"
41
46
  className={cn(
42
- "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
47
+ "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
43
48
  className
44
49
  )}
45
50
  {...props}
@@ -55,7 +60,7 @@ function AlertDescription({
55
60
  <div
56
61
  data-slot="alert-description"
57
62
  className={cn(
58
- "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
63
+ "text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
59
64
  className
60
65
  )}
61
66
  {...props}
@@ -63,14 +68,4 @@ function AlertDescription({
63
68
  )
64
69
  }
65
70
 
66
- function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
67
- return (
68
- <div
69
- data-slot="alert-action"
70
- className={cn("absolute top-2 right-2", className)}
71
- {...props}
72
- />
73
- )
74
- }
75
-
76
- export { Alert, AlertTitle, AlertDescription, AlertAction }
71
+ export { Alert, AlertTitle, AlertDescription }