@openconsole/shadcn 0.0.1 → 0.2.1
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.
- package/README.md +380 -0
- package/package.json +1 -1
- package/styles.css +11 -9
- package/tsconfig.tsbuildinfo +1 -1
- package/skill/SKILL.md +0 -620
- package/skill/customization.md +0 -301
- package/skill/rules/base-vs-radix.md +0 -167
- package/skill/rules/composition.md +0 -240
- package/skill/rules/forms.md +0 -271
- package/skill/rules/icons.md +0 -136
- package/skill/rules/styling.md +0 -180
package/skill/customization.md
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
# 主题化 & 定制
|
|
2
|
-
|
|
3
|
-
组件引用语义化的 CSS 变量。改变量就等于改所有组件。
|
|
4
|
-
|
|
5
|
-
## 目录
|
|
6
|
-
|
|
7
|
-
- 接入(一次 @import 搞定)
|
|
8
|
-
- 工作原理(CSS 变量 → Tailwind 工具类 → 组件)
|
|
9
|
-
- 颜色变量与 OKLCH 格式
|
|
10
|
-
- 暗色模式配置
|
|
11
|
-
- 切主题(覆盖默认 token 或粘 CSS)
|
|
12
|
-
- 新增自定义色(Tailwind v4)
|
|
13
|
-
- 圆角 `--radius`
|
|
14
|
-
- view-transition 变量(圆形展开切主题)
|
|
15
|
-
- 定制组件的边界(只能从外面来)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 接入
|
|
20
|
-
|
|
21
|
-
在 app 的全局 CSS(一般是 `app/globals.css`)里 @import 本包的 styles.css:
|
|
22
|
-
|
|
23
|
-
```css
|
|
24
|
-
@import "tailwindcss";
|
|
25
|
-
@import "@openconsole/shadcn/styles.css";
|
|
26
|
-
@import "@openconsole/atoms/styles.css"; /* 用到 atoms 时 */
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
`@openconsole/shadcn/styles.css` 自带:
|
|
30
|
-
|
|
31
|
-
- `@source` 指令注册自己的源码(Tailwind 自动扫到所有组件用到的工具类)
|
|
32
|
-
- 完整的 `:root` / `.dark` 主题 token 默认值
|
|
33
|
-
- `@theme inline` 映射(让 token 变成 `bg-primary` / `text-muted-foreground` 等工具类)
|
|
34
|
-
- `@custom-variant dark`
|
|
35
|
-
- `tw-animate-css` 动画工具
|
|
36
|
-
- base 层 reset
|
|
37
|
-
|
|
38
|
-
**消费方不需要重复定义 token、不需要手写 `@source`**。要覆盖 token 在
|
|
39
|
-
`@import` 之后重新声明同名变量即可(见下面 [切主题](#切主题))。
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 工作原理
|
|
44
|
-
|
|
45
|
-
1. CSS 变量定义在 `:root`(亮)和 `.dark`(暗)—— 默认值在
|
|
46
|
-
`@openconsole/shadcn/styles.css` 里。
|
|
47
|
-
2. Tailwind v4 把它们映射成工具类: `bg-primary`、`text-muted-foreground` 等。
|
|
48
|
-
3. 组件用这些工具类 —— **改一个变量,所有引用它的组件全跟着变**。
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## 颜色变量
|
|
53
|
-
|
|
54
|
-
每种颜色都遵循 `name` / `name-foreground` 约定。基础变量给背景用,
|
|
55
|
-
`-foreground` 给该背景上的文字 / 图标用。
|
|
56
|
-
|
|
57
|
-
| 变量 | 用途 |
|
|
58
|
-
|---|---|
|
|
59
|
-
| `--background` / `--foreground` | 页面背景与默认文字 |
|
|
60
|
-
| `--card` / `--card-foreground` | 卡片表面 |
|
|
61
|
-
| `--primary` / `--primary-foreground` | 主按钮与主操作 |
|
|
62
|
-
| `--secondary` / `--secondary-foreground` | 次要操作 |
|
|
63
|
-
| `--muted` / `--muted-foreground` | 弱化 / 禁用状态 |
|
|
64
|
-
| `--accent` / `--accent-foreground` | 悬浮与点缀 |
|
|
65
|
-
| `--destructive` / `--destructive-foreground` | 错误与破坏性操作 |
|
|
66
|
-
| `--border` | 默认边框 |
|
|
67
|
-
| `--input` | 表单输入边框 |
|
|
68
|
-
| `--ring` | 焦点环 |
|
|
69
|
-
| `--chart-1` 到 `--chart-5` | 图表 / 可视化 |
|
|
70
|
-
| `--sidebar-*` | 侧边栏专用色 |
|
|
71
|
-
| `--surface` / `--surface-foreground` | 次级表面 |
|
|
72
|
-
|
|
73
|
-
颜色用 **OKLCH**: `--primary: oklch(0.205 0 0)`。三个值依次是 lightness
|
|
74
|
-
(0–1)、chroma(0 表示灰)、hue(0–360)。
|
|
75
|
-
|
|
76
|
-
---
|
|
77
|
-
|
|
78
|
-
## 暗色模式
|
|
79
|
-
|
|
80
|
-
通过根元素上的 `.dark` 类切换。用 `next-themes` 的 `ThemeProvider`
|
|
81
|
-
包根:
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
"use client";
|
|
85
|
-
import { ThemeProvider } from "next-themes";
|
|
86
|
-
|
|
87
|
-
// app/layout.tsx 或类似根布局
|
|
88
|
-
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
|
89
|
-
{children}
|
|
90
|
-
</ThemeProvider>
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
主题切换按钮自己写一个,调用 `useTheme()`:
|
|
94
|
-
|
|
95
|
-
```tsx
|
|
96
|
-
"use client";
|
|
97
|
-
import { Moon, Sun } from "lucide-react";
|
|
98
|
-
import { useTheme } from "next-themes";
|
|
99
|
-
import { Button } from "@openconsole/shadcn";
|
|
100
|
-
|
|
101
|
-
export function ThemeToggle() {
|
|
102
|
-
// 用 resolvedTheme,处于 System 模式时也能正确翻转。
|
|
103
|
-
const { resolvedTheme, setTheme } = useTheme();
|
|
104
|
-
return (
|
|
105
|
-
<Button
|
|
106
|
-
variant="outline"
|
|
107
|
-
size="icon"
|
|
108
|
-
onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
|
|
109
|
-
>
|
|
110
|
-
<Sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
111
|
-
<Moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
112
|
-
<span className="sr-only">切换主题</span>
|
|
113
|
-
</Button>
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## 切主题
|
|
121
|
-
|
|
122
|
-
### 覆盖默认 token
|
|
123
|
-
|
|
124
|
-
在 app 的全局 CSS 里 `@import "@openconsole/shadcn/styles.css"` **之后**
|
|
125
|
-
重新声明同名变量即可。只列要改的, 没列的保持 shadcn 默认值:
|
|
126
|
-
|
|
127
|
-
```css
|
|
128
|
-
@import "tailwindcss";
|
|
129
|
-
@import "@openconsole/shadcn/styles.css";
|
|
130
|
-
|
|
131
|
-
:root {
|
|
132
|
-
--primary: oklch(0.6 0.18 250); /* 覆盖默认黑色为蓝 */
|
|
133
|
-
--radius: 0.5rem; /* 覆盖默认圆角 */
|
|
134
|
-
}
|
|
135
|
-
.dark {
|
|
136
|
-
--primary: oklch(0.7 0.18 250);
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### 粘 CSS 主题
|
|
141
|
-
|
|
142
|
-
从 <https://ui.shadcn.com/themes> 或 <https://tweakcn.com> 复制出来
|
|
143
|
-
的 `:root { … } .dark { … }`, 同样粘在 `@import` 之后即可。CSS 后定义
|
|
144
|
-
的规则覆盖前面的, 所以默认 token 会被整套替换。
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 新增自定义色
|
|
149
|
-
|
|
150
|
-
加到 app 的全局 CSS 里(同样在 `@import` 之后, 不需要新建 CSS 文件)。
|
|
151
|
-
|
|
152
|
-
```css
|
|
153
|
-
/* 1. 在全局 CSS 文件里定义。 */
|
|
154
|
-
:root {
|
|
155
|
-
--warning: oklch(0.84 0.16 84);
|
|
156
|
-
--warning-foreground: oklch(0.28 0.07 46);
|
|
157
|
-
}
|
|
158
|
-
.dark {
|
|
159
|
-
--warning: oklch(0.41 0.11 46);
|
|
160
|
-
--warning-foreground: oklch(0.99 0.02 95);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/* 2. 用 Tailwind v4 的 @theme inline 注册成工具类。 */
|
|
164
|
-
@theme inline {
|
|
165
|
-
--color-warning: var(--warning);
|
|
166
|
-
--color-warning-foreground: var(--warning-foreground);
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
// 3. 在组件里用。
|
|
172
|
-
<div className="bg-warning text-warning-foreground">Warning</div>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## 圆角
|
|
178
|
-
|
|
179
|
-
`--radius` 全局控制圆角。组件从它派生:
|
|
180
|
-
|
|
181
|
-
| 工具类 | 等价值 |
|
|
182
|
-
|---|---|
|
|
183
|
-
| `rounded-lg` | `var(--radius)` |
|
|
184
|
-
| `rounded-md` | `calc(var(--radius) - 2px)` |
|
|
185
|
-
| `rounded-sm` | `calc(var(--radius) - 4px)` |
|
|
186
|
-
|
|
187
|
-
要在运行时调圆角,直接 `document.documentElement.style.setProperty("--radius", "0.75rem")`。
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## View-transition 变量(圆形展开切主题)
|
|
192
|
-
|
|
193
|
-
切主题时想要从点击位置圆形展开的过渡动画,用 View Transitions API +
|
|
194
|
-
CSS 变量做。给全局 CSS 加:
|
|
195
|
-
|
|
196
|
-
```css
|
|
197
|
-
@supports (view-transition-name: root) {
|
|
198
|
-
::view-transition-new(root) {
|
|
199
|
-
clip-path: circle(0% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%));
|
|
200
|
-
animation: vt-circle-in 350ms ease-out forwards;
|
|
201
|
-
}
|
|
202
|
-
@keyframes vt-circle-in {
|
|
203
|
-
to { clip-path: circle(150% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%)); }
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
切换按钮里在点击时设置原点变量并启动 transition:
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
"use client";
|
|
212
|
-
import { useTheme } from "next-themes";
|
|
213
|
-
import { Button } from "@openconsole/shadcn";
|
|
214
|
-
|
|
215
|
-
type TransitionDocument = Document & {
|
|
216
|
-
startViewTransition?: (cb: () => void) => { finished: Promise<void> };
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
export function ThemeToggle() {
|
|
220
|
-
const { resolvedTheme, setTheme } = useTheme();
|
|
221
|
-
|
|
222
|
-
const onToggle = (e: React.MouseEvent) => {
|
|
223
|
-
const root = document.documentElement;
|
|
224
|
-
root.style.setProperty("--vt-origin-x", `${(e.clientX / window.innerWidth) * 100}%`);
|
|
225
|
-
root.style.setProperty("--vt-origin-y", `${(e.clientY / window.innerHeight) * 100}%`);
|
|
226
|
-
const next = resolvedTheme === "dark" ? "light" : "dark";
|
|
227
|
-
const doc = document as TransitionDocument;
|
|
228
|
-
if (doc.startViewTransition) {
|
|
229
|
-
doc.startViewTransition(() => setTheme(next));
|
|
230
|
-
} else {
|
|
231
|
-
setTheme(next);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
return <Button variant="outline" size="icon" onClick={onToggle}>…</Button>;
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
> 变量名用 `--vt-origin-x` / `--vt-origin-y` 而不是 `--x` / `--y` ——
|
|
240
|
-
> 太通用的名字容易跟其他库的临时变量冲突。
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## 定制组件的边界
|
|
245
|
-
|
|
246
|
-
`@openconsole/shadcn` 是只读消费包 —— 你**不能**改组件源码加 variant、
|
|
247
|
-
不能 fork 文件、不能 patch。能做的就是从外面调整:
|
|
248
|
-
|
|
249
|
-
### 1. 内置 variant(优先)
|
|
250
|
-
|
|
251
|
-
```tsx
|
|
252
|
-
<Button variant="outline" size="sm">Click</Button>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
`Button` variant: `default` / `secondary` / `outline` / `ghost` /
|
|
256
|
-
`destructive` / `link`。`Badge` variant: `default` / `secondary` /
|
|
257
|
-
`destructive` / `outline`。其它原语的 variant hover 上去看 IDE 提示。
|
|
258
|
-
|
|
259
|
-
### 2. `className` 加 Tailwind 类(仅布局)
|
|
260
|
-
|
|
261
|
-
```tsx
|
|
262
|
-
<Card className="mx-auto max-w-md">…</Card>
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
**不要**用 `className` 覆盖颜色或排版 —— 改主题变量。
|
|
266
|
-
|
|
267
|
-
### 3. CSS 变量(颜色 / 圆角 / 字体)
|
|
268
|
-
|
|
269
|
-
见上面 [新增自定义色](#新增自定义色) 和 [圆角](#圆角)。
|
|
270
|
-
|
|
271
|
-
### 4. wrapper 组件(应用层抽象)
|
|
272
|
-
|
|
273
|
-
应用里要 `ConfirmDialog`、`PageHeader`、`Toolbar` 这种复合形态,在自己
|
|
274
|
-
的应用代码里拼:
|
|
275
|
-
|
|
276
|
-
```tsx
|
|
277
|
-
// 在你的应用代码里
|
|
278
|
-
import {
|
|
279
|
-
AlertDialog, AlertDialogTrigger, AlertDialogContent,
|
|
280
|
-
AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
|
|
281
|
-
AlertDialogFooter, AlertDialogCancel, AlertDialogAction,
|
|
282
|
-
} from "@openconsole/shadcn";
|
|
283
|
-
|
|
284
|
-
export function ConfirmDialog({ title, description, onConfirm, children }) {
|
|
285
|
-
return (
|
|
286
|
-
<AlertDialog>
|
|
287
|
-
<AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
|
|
288
|
-
<AlertDialogContent>
|
|
289
|
-
<AlertDialogHeader>
|
|
290
|
-
<AlertDialogTitle>{title}</AlertDialogTitle>
|
|
291
|
-
<AlertDialogDescription>{description}</AlertDialogDescription>
|
|
292
|
-
</AlertDialogHeader>
|
|
293
|
-
<AlertDialogFooter>
|
|
294
|
-
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
295
|
-
<AlertDialogAction onClick={onConfirm}>Confirm</AlertDialogAction>
|
|
296
|
-
</AlertDialogFooter>
|
|
297
|
-
</AlertDialogContent>
|
|
298
|
-
</AlertDialog>
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
```
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# 本包 API 速查
|
|
2
|
-
|
|
3
|
-
本包导出的组件有几处 prop 形状容易写错,这份文件作为速查清单。
|
|
4
|
-
照下面的写法用就对了。
|
|
5
|
-
|
|
6
|
-
## 目录
|
|
7
|
-
|
|
8
|
-
- 自定义触发器: `asChild`
|
|
9
|
-
- `Select`
|
|
10
|
-
- `ToggleGroup`
|
|
11
|
-
- `Slider`
|
|
12
|
-
- `Accordion`
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 自定义触发器: `asChild`
|
|
17
|
-
|
|
18
|
-
要让 trigger / close 渲染成自定义元素或别的组件(最常见的是 `Button`),
|
|
19
|
-
用 `asChild` 替换默认元素。**不要**用多余的元素包裹 trigger。
|
|
20
|
-
|
|
21
|
-
**Incorrect:**
|
|
22
|
-
|
|
23
|
-
```tsx
|
|
24
|
-
<DialogTrigger>
|
|
25
|
-
<div>
|
|
26
|
-
<Button>Open</Button>
|
|
27
|
-
</div>
|
|
28
|
-
</DialogTrigger>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Correct:**
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
<DialogTrigger asChild>
|
|
35
|
-
<Button>Open</Button>
|
|
36
|
-
</DialogTrigger>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
要把 trigger 渲染成 `<a>` 这种非 button 元素,同样直接 `asChild`:
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
<Button asChild>
|
|
43
|
-
<a href="/docs">Read the docs</a>
|
|
44
|
-
</Button>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
适用于所有 trigger / close 组件: `DialogTrigger`、`SheetTrigger`、
|
|
48
|
-
`AlertDialogTrigger`、`DropdownMenuTrigger`、`PopoverTrigger`、
|
|
49
|
-
`TooltipTrigger`、`CollapsibleTrigger`、`DialogClose`、`SheetClose`、
|
|
50
|
-
`NavigationMenuLink`、`BreadcrumbLink`、`SidebarMenuButton`。
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
## `Select`
|
|
55
|
-
|
|
56
|
-
inline `<SelectItem>`,**不要**通过 `items` 数组传入。
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
<Select>
|
|
60
|
-
<SelectTrigger>
|
|
61
|
-
<SelectValue placeholder="Select a fruit" />
|
|
62
|
-
</SelectTrigger>
|
|
63
|
-
<SelectContent>
|
|
64
|
-
<SelectGroup>
|
|
65
|
-
<SelectItem value="apple">Apple</SelectItem>
|
|
66
|
-
<SelectItem value="banana">Banana</SelectItem>
|
|
67
|
-
</SelectGroup>
|
|
68
|
-
</SelectContent>
|
|
69
|
-
</Select>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
要点:
|
|
73
|
-
- **Placeholder**: 写在 `<SelectValue placeholder="…" />` 上。
|
|
74
|
-
- **定位**: `<SelectContent position="popper">`。
|
|
75
|
-
- **value 类型**: 必须是字符串。
|
|
76
|
-
|
|
77
|
-
> 需要 multi-select 或对象 value: 本包的 `Select` 不支持。在应用层
|
|
78
|
-
> 用 `Command` + `Popover` + `Checkbox` 自己拼。
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## `ToggleGroup`
|
|
83
|
-
|
|
84
|
-
必须显式 `type="single"` 或 `type="multiple"`:
|
|
85
|
-
|
|
86
|
-
```tsx
|
|
87
|
-
// 单选, defaultValue 是字符串
|
|
88
|
-
<ToggleGroup type="single" defaultValue="daily" spacing={2}>
|
|
89
|
-
<ToggleGroupItem value="daily">Daily</ToggleGroupItem>
|
|
90
|
-
<ToggleGroupItem value="weekly">Weekly</ToggleGroupItem>
|
|
91
|
-
</ToggleGroup>
|
|
92
|
-
|
|
93
|
-
// 多选, defaultValue 是字符串数组
|
|
94
|
-
<ToggleGroup type="multiple">
|
|
95
|
-
<ToggleGroupItem value="bold">Bold</ToggleGroupItem>
|
|
96
|
-
<ToggleGroupItem value="italic">Italic</ToggleGroupItem>
|
|
97
|
-
</ToggleGroup>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
受控单选:
|
|
101
|
-
|
|
102
|
-
```tsx
|
|
103
|
-
const [value, setValue] = React.useState("normal");
|
|
104
|
-
<ToggleGroup type="single" value={value} onValueChange={setValue}>
|
|
105
|
-
…
|
|
106
|
-
</ToggleGroup>
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## `Slider`
|
|
112
|
-
|
|
113
|
-
`defaultValue` / `value` **总是数组**:
|
|
114
|
-
|
|
115
|
-
```tsx
|
|
116
|
-
// 单滑块
|
|
117
|
-
<Slider defaultValue={[50]} max={100} step={1} />
|
|
118
|
-
|
|
119
|
-
// 区间
|
|
120
|
-
<Slider defaultValue={[20, 80]} max={100} step={1} />
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
受控:
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
const [value, setValue] = React.useState([0.3, 0.7]);
|
|
127
|
-
<Slider value={value} onValueChange={setValue} />
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## `Accordion`
|
|
133
|
-
|
|
134
|
-
必须显式 `type="single"` 或 `type="multiple"`。单选支持 `collapsible`,
|
|
135
|
-
`defaultValue` 是字符串:
|
|
136
|
-
|
|
137
|
-
```tsx
|
|
138
|
-
// 单选, 可折叠
|
|
139
|
-
<Accordion type="single" collapsible defaultValue="item-1">
|
|
140
|
-
<AccordionItem value="item-1">…</AccordionItem>
|
|
141
|
-
</Accordion>
|
|
142
|
-
|
|
143
|
-
// 多选, defaultValue 是字符串数组
|
|
144
|
-
<Accordion type="multiple" defaultValue={["item-1", "item-2"]}>
|
|
145
|
-
<AccordionItem value="item-1">…</AccordionItem>
|
|
146
|
-
<AccordionItem value="item-2">…</AccordionItem>
|
|
147
|
-
</Accordion>
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## 错写形态速查
|
|
153
|
-
|
|
154
|
-
下面这些 prop 形态**不是本包的 API**,写出来跑不通 —— 按右列改:
|
|
155
|
-
|
|
156
|
-
| 错写形态 | 正确形态 |
|
|
157
|
-
|---|---|
|
|
158
|
-
| `<XTrigger render={<Button />} />` | `<XTrigger asChild><Button /></XTrigger>` |
|
|
159
|
-
| `nativeButton={false}` | 不需要,`asChild` 自动处理 |
|
|
160
|
-
| `<Select items={[…]}>` + `<SelectValue>{(v) => …}</SelectValue>` | inline `<SelectItem>`,placeholder 在 `<SelectValue>` |
|
|
161
|
-
| `<SelectContent alignItemWithTrigger={false}>` | `<SelectContent position="popper">` |
|
|
162
|
-
| `itemToStringValue` | 不支持 —— 用 `Command` + `Popover` + `Checkbox` 自己拼 |
|
|
163
|
-
| `<ToggleGroup multiple>` | `<ToggleGroup type="multiple">` |
|
|
164
|
-
| 单选 `<ToggleGroup defaultValue={["x"]}>` | `<ToggleGroup type="single" defaultValue="x">` |
|
|
165
|
-
| `<Slider defaultValue={50} />` | `<Slider defaultValue={[50]} />` |
|
|
166
|
-
| `<Accordion>` 没有 `type` | 加 `type="single"` 或 `type="multiple"` |
|
|
167
|
-
| 单选 `<Accordion defaultValue={["x"]}>` | `<Accordion type="single" defaultValue="x">` |
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
# 组件组合
|
|
2
|
-
|
|
3
|
-
## 目录
|
|
4
|
-
|
|
5
|
-
- Item 一定在自己的 Group 里
|
|
6
|
-
- 提示框用 `Alert`
|
|
7
|
-
- 空状态用 `Empty`
|
|
8
|
-
- Toast 用 sonner
|
|
9
|
-
- 在不同 overlay 之间选
|
|
10
|
-
- `Dialog` / `Sheet` / `Drawer` 一定要有 Title
|
|
11
|
-
- `Card` 用完整组合
|
|
12
|
-
- `Button` 没有 `isPending` / `isLoading` prop
|
|
13
|
-
- `TabsTrigger` 必须在 `TabsList` 里
|
|
14
|
-
- `Avatar` 必须带 `AvatarFallback`
|
|
15
|
-
- 用组件,别堆裸标签
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Item 一定在自己的 Group 里
|
|
20
|
-
|
|
21
|
-
**永远不要**把 Item 直接渲染在 content 容器里。
|
|
22
|
-
|
|
23
|
-
**Incorrect:**
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
<SelectContent>
|
|
27
|
-
<SelectItem value="apple">Apple</SelectItem>
|
|
28
|
-
<SelectItem value="banana">Banana</SelectItem>
|
|
29
|
-
</SelectContent>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Correct:**
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
<SelectContent>
|
|
36
|
-
<SelectGroup>
|
|
37
|
-
<SelectItem value="apple">Apple</SelectItem>
|
|
38
|
-
<SelectItem value="banana">Banana</SelectItem>
|
|
39
|
-
</SelectGroup>
|
|
40
|
-
</SelectContent>
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
适用于所有基于 group 的组件:
|
|
44
|
-
|
|
45
|
-
| Item | Group |
|
|
46
|
-
|---|---|
|
|
47
|
-
| `SelectItem`、`SelectLabel` | `SelectGroup` |
|
|
48
|
-
| `DropdownMenuItem`、`DropdownMenuLabel`、`DropdownMenuSub` | `DropdownMenuGroup` |
|
|
49
|
-
| `MenubarItem` | `MenubarGroup` |
|
|
50
|
-
| `ContextMenuItem` | `ContextMenuGroup` |
|
|
51
|
-
| `CommandItem` | `CommandGroup` |
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## 提示框用 `Alert`
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
import { Alert, AlertTitle, AlertDescription } from "@openconsole/shadcn";
|
|
59
|
-
|
|
60
|
-
<Alert>
|
|
61
|
-
<AlertTitle>Warning</AlertTitle>
|
|
62
|
-
<AlertDescription>Something needs attention.</AlertDescription>
|
|
63
|
-
</Alert>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## 空状态用 `Empty`
|
|
69
|
-
|
|
70
|
-
```tsx
|
|
71
|
-
import {
|
|
72
|
-
Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent,
|
|
73
|
-
Button,
|
|
74
|
-
} from "@openconsole/shadcn";
|
|
75
|
-
|
|
76
|
-
<Empty>
|
|
77
|
-
<EmptyHeader>
|
|
78
|
-
<EmptyMedia variant="icon"><FolderIcon /></EmptyMedia>
|
|
79
|
-
<EmptyTitle>No projects yet</EmptyTitle>
|
|
80
|
-
<EmptyDescription>Get started by creating a new project.</EmptyDescription>
|
|
81
|
-
</EmptyHeader>
|
|
82
|
-
<EmptyContent>
|
|
83
|
-
<Button>Create Project</Button>
|
|
84
|
-
</EmptyContent>
|
|
85
|
-
</Empty>
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Toast 用 sonner
|
|
91
|
-
|
|
92
|
-
`toast()` 从 sonner 直接导入,不在本包里:
|
|
93
|
-
|
|
94
|
-
```tsx
|
|
95
|
-
import { toast } from "sonner";
|
|
96
|
-
|
|
97
|
-
toast.success("Changes saved.");
|
|
98
|
-
toast.error("Something went wrong.");
|
|
99
|
-
toast("File deleted.", {
|
|
100
|
-
action: { label: "Undo", onClick: () => undoDelete() },
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
在 app 根上挂一次本包的 `<Toaster />`:
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
import { Toaster } from "@openconsole/shadcn";
|
|
108
|
-
|
|
109
|
-
// app/layout.tsx
|
|
110
|
-
<body>
|
|
111
|
-
{children}
|
|
112
|
-
<Toaster />
|
|
113
|
-
</body>
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
`Toaster` 默认带 success / info / warning / error / loading 五种状态图标
|
|
117
|
-
和适配主题的颜色 —— 不用再传 `theme` 或 `icons`。
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 在不同 overlay 之间选
|
|
122
|
-
|
|
123
|
-
| 场景 | 用什么 |
|
|
124
|
-
|---|---|
|
|
125
|
-
| 需要输入的聚焦任务 | `Dialog` |
|
|
126
|
-
| 破坏性操作的二次确认 | `AlertDialog` |
|
|
127
|
-
| 带详情或筛选的侧拉面板 | `Sheet` |
|
|
128
|
-
| 移动端优先的底部面板 | `Drawer` |
|
|
129
|
-
| 悬浮时的快速信息 | `HoverCard` |
|
|
130
|
-
| 点击触发的小块上下文内容 | `Popover` |
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## `Dialog` / `Sheet` / `Drawer` 一定要有 Title
|
|
135
|
-
|
|
136
|
-
`DialogTitle`、`SheetTitle`、`DrawerTitle` 对屏幕阅读器是必需的。
|
|
137
|
-
视觉上不想显示就 `className="sr-only"`。
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
<DialogContent>
|
|
141
|
-
<DialogHeader>
|
|
142
|
-
<DialogTitle>Edit Profile</DialogTitle>
|
|
143
|
-
<DialogDescription>Update your profile.</DialogDescription>
|
|
144
|
-
</DialogHeader>
|
|
145
|
-
...
|
|
146
|
-
</DialogContent>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
不需要可见 header 的也要带:
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
<DialogContent>
|
|
153
|
-
<DialogTitle className="sr-only">Settings</DialogTitle>
|
|
154
|
-
{/* … */}
|
|
155
|
-
</DialogContent>
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
---
|
|
159
|
-
|
|
160
|
-
## `Card` 用完整组合
|
|
161
|
-
|
|
162
|
-
**永远不要**把所有东西塞到 `CardContent` 里:
|
|
163
|
-
|
|
164
|
-
```tsx
|
|
165
|
-
<Card>
|
|
166
|
-
<CardHeader>
|
|
167
|
-
<CardTitle>Team Members</CardTitle>
|
|
168
|
-
<CardDescription>Manage your team.</CardDescription>
|
|
169
|
-
</CardHeader>
|
|
170
|
-
<CardContent>...</CardContent>
|
|
171
|
-
<CardFooter>
|
|
172
|
-
<Button>Invite</Button>
|
|
173
|
-
</CardFooter>
|
|
174
|
-
</Card>
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## `Button` 没有 `isPending` / `isLoading` prop
|
|
180
|
-
|
|
181
|
-
用 `Spinner` + `data-icon` + `disabled` 拼:
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
import { Button, Spinner } from "@openconsole/shadcn";
|
|
185
|
-
|
|
186
|
-
<Button disabled>
|
|
187
|
-
<Spinner data-icon="inline-start" />
|
|
188
|
-
Saving...
|
|
189
|
-
</Button>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
带 mutation hook 时:
|
|
193
|
-
|
|
194
|
-
```tsx
|
|
195
|
-
<Button disabled={mutation.isPending}>
|
|
196
|
-
{mutation.isPending && <Spinner data-icon="inline-start" />}
|
|
197
|
-
Save
|
|
198
|
-
</Button>
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## `TabsTrigger` 必须在 `TabsList` 里
|
|
204
|
-
|
|
205
|
-
**永远不要**把 `TabsTrigger` 直接渲染在 `Tabs` 里 —— 一定包在 `TabsList`:
|
|
206
|
-
|
|
207
|
-
```tsx
|
|
208
|
-
<Tabs defaultValue="account">
|
|
209
|
-
<TabsList>
|
|
210
|
-
<TabsTrigger value="account">Account</TabsTrigger>
|
|
211
|
-
<TabsTrigger value="password">Password</TabsTrigger>
|
|
212
|
-
</TabsList>
|
|
213
|
-
<TabsContent value="account">...</TabsContent>
|
|
214
|
-
</Tabs>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## `Avatar` 必须带 `AvatarFallback`
|
|
220
|
-
|
|
221
|
-
图片加载失败的兜底,必须有:
|
|
222
|
-
|
|
223
|
-
```tsx
|
|
224
|
-
<Avatar>
|
|
225
|
-
<AvatarImage src="/avatar.png" alt="User" />
|
|
226
|
-
<AvatarFallback>JD</AvatarFallback>
|
|
227
|
-
</Avatar>
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## 用组件,别堆裸标签
|
|
233
|
-
|
|
234
|
-
| 不要 | 改用 |
|
|
235
|
-
|---|---|
|
|
236
|
-
| `<hr>` 或 `<div className="border-t">` | `<Separator />` |
|
|
237
|
-
| `<div className="animate-pulse">` 加样式 | `<Skeleton className="h-4 w-3/4" />` |
|
|
238
|
-
| `<span className="rounded-full bg-green-100 …">` | `<Badge variant="secondary">` |
|
|
239
|
-
| 自己写 CSS 转圈 | `<Spinner />` |
|
|
240
|
-
| 自己加 `<kbd>` 样式 | `<Kbd>` |
|