@coze-arch/cli 0.0.6 → 0.0.7
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/lib/__templates__/taro/eslint.config.mjs +52 -2
- package/lib/__templates__/taro/src/app.tsx +2 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +4 -4
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +1 -1
- package/lib/__templates__/taro/src/components/ui/toast.tsx +1 -1
- package/lib/__templates__/taro/src/presets/env.ts +1 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +2 -1
- package/lib/__templates__/taro/src/presets/h5-error-boundary.tsx +391 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +4 -3
- package/lib/__templates__/taro/src/presets/h5-styles.ts +3 -1
- package/lib/__templates__/taro/src/presets/index.tsx +15 -2
- package/lib/cli.js +1 -1
- package/package.json +1 -1
- package/lib/__templates__/taro/components.md +0 -1686
|
@@ -1,1686 +0,0 @@
|
|
|
1
|
-
# Taro Shadcn UI 组件库 Skill
|
|
2
|
-
|
|
3
|
-
> shadcn/ui 的 Taro 移植版,支持微信小程序、抖音小程序和 H5 多端运行。所有组件基于 Taro 4 + React 18 + Tailwind CSS 构建,不依赖 Radix UI 等 Web-only 库,而是从零实现了全部交互逻辑。
|
|
4
|
-
|
|
5
|
-
## 技术栈
|
|
6
|
-
|
|
7
|
-
- **框架**: Taro 4.1.9 + React 18
|
|
8
|
-
- **样式**: Tailwind CSS 4 + `tailwindcss-animate` + CSS Variables (lab 色彩空间)
|
|
9
|
-
- **变体系统**: `class-variance-authority` (cva)
|
|
10
|
-
- **类名合并**: `clsx` + `tailwind-merge` → 封装为 `cn()` 工具函数
|
|
11
|
-
- **图标**: `lucide-react-taro` (Lucide 的 Taro 适配版,非 `lucide-react`)
|
|
12
|
-
- **日期**: `date-fns`
|
|
13
|
-
- **状态管理**: React Context + `useState` / `useRef`(组件内部使用,不依赖外部状态库)
|
|
14
|
-
|
|
15
|
-
## 项目结构
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
src/
|
|
19
|
-
├── components/ui/ # 所有 UI 组件(49 个)
|
|
20
|
-
├── lib/
|
|
21
|
-
│ ├── utils.ts # cn() 类名合并工具
|
|
22
|
-
│ ├── platform.ts # isH5() 平台检测
|
|
23
|
-
│ ├── measure.ts # getRectById / getViewport 元素尺寸测量
|
|
24
|
-
│ └── hooks/
|
|
25
|
-
│ └── use-keyboard-offset.ts # 虚拟键盘偏移处理
|
|
26
|
-
├── app.css # CSS Variables 主题定义
|
|
27
|
-
└── pages/ # 页面
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## 核心工具函数
|
|
31
|
-
|
|
32
|
-
```tsx
|
|
33
|
-
// @/lib/utils
|
|
34
|
-
import { clsx, type ClassValue } from "clsx"
|
|
35
|
-
import { twMerge } from "tailwind-merge"
|
|
36
|
-
|
|
37
|
-
export function cn(...inputs: ClassValue[]) {
|
|
38
|
-
return twMerge(clsx(inputs))
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
```tsx
|
|
43
|
-
// @/lib/platform — 平台检测
|
|
44
|
-
import Taro from "@tarojs/taro"
|
|
45
|
-
export const isH5 = () => Taro.getEnv() === Taro.ENV_TYPE.WEB
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## 关键设计模式
|
|
49
|
-
|
|
50
|
-
### 1. 平台原语:使用 Taro 组件而非 HTML 元素
|
|
51
|
-
|
|
52
|
-
所有组件使用 `@tarojs/components` 中的 `View`、`Text`、`Image`、`Input`、`ScrollView`、`Swiper` 等代替 HTML 原生元素:
|
|
53
|
-
|
|
54
|
-
```tsx
|
|
55
|
-
// ✅ 正确 — 使用 Taro View
|
|
56
|
-
import { View } from "@tarojs/components"
|
|
57
|
-
<View className="flex items-center gap-2">...</View>
|
|
58
|
-
|
|
59
|
-
// ❌ 错误 — 不要使用 HTML div
|
|
60
|
-
<div className="flex items-center gap-2">...</div>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### 2. 受控/非受控双模式
|
|
64
|
-
|
|
65
|
-
大部分有状态组件同时支持受控和非受控模式:
|
|
66
|
-
|
|
67
|
-
```tsx
|
|
68
|
-
// 非受控 — 使用 defaultValue
|
|
69
|
-
<Select defaultValue="apple">...</Select>
|
|
70
|
-
|
|
71
|
-
// 受控 — 使用 value + onValueChange
|
|
72
|
-
const [val, setVal] = useState("apple")
|
|
73
|
-
<Select value={val} onValueChange={setVal}>...</Select>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
此模式适用于:`Accordion`、`Checkbox`、`Collapsible`、`Dialog`、`Drawer`、`HoverCard`、`Popover`、`RadioGroup`、`Select`、`Sheet`、`Slider`、`Switch`、`Tabs`、`Toggle`、`ToggleGroup`、`Tooltip`。
|
|
77
|
-
|
|
78
|
-
### 3. Portal 渲染
|
|
79
|
-
|
|
80
|
-
弹出层组件(Dialog、Drawer、Sheet、Popover、Tooltip、DropdownMenu 等)通过 `Portal` 组件在视口顶层渲染:
|
|
81
|
-
- H5 端:`createPortal(children, document.body)`
|
|
82
|
-
- 小程序端:`<RootPortal>{children}</RootPortal>`
|
|
83
|
-
|
|
84
|
-
### 4. 位置计算与碰撞避免
|
|
85
|
-
|
|
86
|
-
`Popover`、`Tooltip`、`DropdownMenu`、`HoverCard`、`Select` 使用 `getRectById` + `getViewport` 测量元素尺寸,自动计算弹出位置,避免超出视口。
|
|
87
|
-
|
|
88
|
-
### 5. hoverClass 代替 CSS hover
|
|
89
|
-
|
|
90
|
-
Taro 小程序不支持 CSS `:hover`,组件通过 `hoverClass` 属性实现按下态:
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
<View hoverClass="bg-accent" ...>
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### 6. 图标库
|
|
97
|
-
|
|
98
|
-
使用 `lucide-react-taro` 而非 `lucide-react`:
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
import { ChevronRight, Mail, X } from "lucide-react-taro"
|
|
102
|
-
<Mail size={16} />
|
|
103
|
-
<X size={16} color="#737373" />
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 全部 49 个组件用法
|
|
109
|
-
|
|
110
|
-
### Accordion 手风琴
|
|
111
|
-
|
|
112
|
-
支持单项展开 (`type="single"`) 和多项展开 (`type="multiple"`)。
|
|
113
|
-
|
|
114
|
-
```tsx
|
|
115
|
-
import {
|
|
116
|
-
Accordion, AccordionContent, AccordionItem, AccordionTrigger,
|
|
117
|
-
} from "@/components/ui/accordion"
|
|
118
|
-
|
|
119
|
-
<Accordion type="single" collapsible className="w-full">
|
|
120
|
-
<AccordionItem value="item-1">
|
|
121
|
-
<AccordionTrigger>标题一</AccordionTrigger>
|
|
122
|
-
<AccordionContent>内容一</AccordionContent>
|
|
123
|
-
</AccordionItem>
|
|
124
|
-
<AccordionItem value="item-2">
|
|
125
|
-
<AccordionTrigger>标题二</AccordionTrigger>
|
|
126
|
-
<AccordionContent>内容二</AccordionContent>
|
|
127
|
-
</AccordionItem>
|
|
128
|
-
</Accordion>
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
| 属性 | 类型 | 说明 |
|
|
132
|
-
| --------------- | ------------------------ | -------------------- |
|
|
133
|
-
| `type` | `"single" \| "multiple"` | 展开模式 |
|
|
134
|
-
| `collapsible` | `boolean` | 单项模式下是否可折叠 |
|
|
135
|
-
| `value` | `string \| string[]` | 受控展开值 |
|
|
136
|
-
| `defaultValue` | `string \| string[]` | 默认展开值 |
|
|
137
|
-
| `onValueChange` | `(value) => void` | 展开项变化回调 |
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
### AlertDialog 警告对话框
|
|
142
|
-
|
|
143
|
-
不可通过点击遮罩关闭,必须通过 Action 或 Cancel 按钮关闭。
|
|
144
|
-
|
|
145
|
-
```tsx
|
|
146
|
-
import {
|
|
147
|
-
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
|
148
|
-
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader,
|
|
149
|
-
AlertDialogTitle, AlertDialogTrigger,
|
|
150
|
-
} from "@/components/ui/alert-dialog"
|
|
151
|
-
import { Button } from "@/components/ui/button"
|
|
152
|
-
|
|
153
|
-
<AlertDialog>
|
|
154
|
-
<AlertDialogTrigger>
|
|
155
|
-
<Button variant="outline">删除账户</Button>
|
|
156
|
-
</AlertDialogTrigger>
|
|
157
|
-
<AlertDialogContent>
|
|
158
|
-
<AlertDialogHeader>
|
|
159
|
-
<AlertDialogTitle>确定要删除吗?</AlertDialogTitle>
|
|
160
|
-
<AlertDialogDescription>
|
|
161
|
-
此操作不可撤销。
|
|
162
|
-
</AlertDialogDescription>
|
|
163
|
-
</AlertDialogHeader>
|
|
164
|
-
<AlertDialogFooter>
|
|
165
|
-
<AlertDialogCancel>取消</AlertDialogCancel>
|
|
166
|
-
<AlertDialogAction>继续</AlertDialogAction>
|
|
167
|
-
</AlertDialogFooter>
|
|
168
|
-
</AlertDialogContent>
|
|
169
|
-
</AlertDialog>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
### Alert 警告提示
|
|
175
|
-
|
|
176
|
-
纯展示组件,支持 `default` 和 `destructive` 两种变体。
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
|
180
|
-
import { Terminal, CircleAlert } from "lucide-react-taro"
|
|
181
|
-
|
|
182
|
-
<Alert>
|
|
183
|
-
<Terminal size={16} />
|
|
184
|
-
<AlertTitle>提示</AlertTitle>
|
|
185
|
-
<AlertDescription>你可以通过 CLI 添加组件。</AlertDescription>
|
|
186
|
-
</Alert>
|
|
187
|
-
|
|
188
|
-
<Alert variant="destructive">
|
|
189
|
-
<CircleAlert color="#e7000b" size={16} />
|
|
190
|
-
<AlertTitle>错误</AlertTitle>
|
|
191
|
-
<AlertDescription>会话已过期,请重新登录。</AlertDescription>
|
|
192
|
-
</Alert>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
### AspectRatio 宽高比
|
|
198
|
-
|
|
199
|
-
通过 `ratio` 属性控制子元素的宽高比。
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
import { AspectRatio } from "@/components/ui/aspect-ratio"
|
|
203
|
-
import { Image } from "@tarojs/components"
|
|
204
|
-
|
|
205
|
-
<AspectRatio ratio={16 / 9} className="bg-muted">
|
|
206
|
-
<Image
|
|
207
|
-
src="https://example.com/photo.jpg"
|
|
208
|
-
className="h-full w-full rounded-md object-cover"
|
|
209
|
-
mode="aspectFill"
|
|
210
|
-
/>
|
|
211
|
-
</AspectRatio>
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
| `ratio` 值 | 效果 |
|
|
215
|
-
| ---------- | -------- |
|
|
216
|
-
| `16 / 9` | 宽屏 |
|
|
217
|
-
| `4 / 3` | 传统比例 |
|
|
218
|
-
| `1 / 1` | 正方形 |
|
|
219
|
-
|
|
220
|
-
---
|
|
221
|
-
|
|
222
|
-
### Avatar 头像
|
|
223
|
-
|
|
224
|
-
图片加载失败时自动显示 Fallback 文字。
|
|
225
|
-
|
|
226
|
-
```tsx
|
|
227
|
-
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
228
|
-
|
|
229
|
-
<Avatar>
|
|
230
|
-
<AvatarImage src="https://github.com/shadcn.png" />
|
|
231
|
-
<AvatarFallback>CN</AvatarFallback>
|
|
232
|
-
</Avatar>
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
### Badge 徽章
|
|
238
|
-
|
|
239
|
-
```tsx
|
|
240
|
-
import { Badge } from "@/components/ui/badge"
|
|
241
|
-
|
|
242
|
-
<Badge>默认</Badge>
|
|
243
|
-
<Badge variant="secondary">次要</Badge>
|
|
244
|
-
<Badge variant="destructive">危险</Badge>
|
|
245
|
-
<Badge variant="outline">轮廓</Badge>
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
**变体**: `default` | `secondary` | `destructive` | `outline`
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
### Breadcrumb 面包屑
|
|
253
|
-
|
|
254
|
-
```tsx
|
|
255
|
-
import {
|
|
256
|
-
Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList,
|
|
257
|
-
BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis,
|
|
258
|
-
} from "@/components/ui/breadcrumb"
|
|
259
|
-
|
|
260
|
-
<Breadcrumb>
|
|
261
|
-
<BreadcrumbList>
|
|
262
|
-
<BreadcrumbItem>
|
|
263
|
-
<BreadcrumbLink href="/">首页</BreadcrumbLink>
|
|
264
|
-
</BreadcrumbItem>
|
|
265
|
-
<BreadcrumbSeparator />
|
|
266
|
-
<BreadcrumbItem>
|
|
267
|
-
<BreadcrumbLink href="/components">组件</BreadcrumbLink>
|
|
268
|
-
</BreadcrumbItem>
|
|
269
|
-
<BreadcrumbSeparator />
|
|
270
|
-
<BreadcrumbItem>
|
|
271
|
-
<BreadcrumbPage>当前页</BreadcrumbPage>
|
|
272
|
-
</BreadcrumbItem>
|
|
273
|
-
</BreadcrumbList>
|
|
274
|
-
</Breadcrumb>
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
### Button 按钮
|
|
280
|
-
|
|
281
|
-
```tsx
|
|
282
|
-
import { Button } from "@/components/ui/button"
|
|
283
|
-
import { Mail, LoaderCircle, ChevronRight } from "lucide-react-taro"
|
|
284
|
-
|
|
285
|
-
{/* 变体 */}
|
|
286
|
-
<Button>默认</Button>
|
|
287
|
-
<Button variant="secondary">次要</Button>
|
|
288
|
-
<Button variant="destructive">危险</Button>
|
|
289
|
-
<Button variant="outline">轮廓</Button>
|
|
290
|
-
<Button variant="ghost">幽灵</Button>
|
|
291
|
-
<Button variant="link">链接</Button>
|
|
292
|
-
|
|
293
|
-
{/* 尺寸 */}
|
|
294
|
-
<Button size="sm">小按钮</Button>
|
|
295
|
-
<Button size="lg">大按钮</Button>
|
|
296
|
-
<Button variant="outline" size="icon">
|
|
297
|
-
<ChevronRight size={16} />
|
|
298
|
-
</Button>
|
|
299
|
-
|
|
300
|
-
{/* 带图标 */}
|
|
301
|
-
<Button>
|
|
302
|
-
<Mail className="mr-2" color="#fff" size={16} /> 邮箱登录
|
|
303
|
-
</Button>
|
|
304
|
-
|
|
305
|
-
{/* 加载状态 */}
|
|
306
|
-
<Button disabled>
|
|
307
|
-
<LoaderCircle className="mr-2 animate-spin" color="#fff" size={16} />
|
|
308
|
-
请稍候
|
|
309
|
-
</Button>
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
| 属性 | 值 |
|
|
313
|
-
| ---------- | --------------------------------------------------------------------------------------- |
|
|
314
|
-
| `variant` | `"default"` \| `"secondary"` \| `"destructive"` \| `"outline"` \| `"ghost"` \| `"link"` |
|
|
315
|
-
| `size` | `"default"` \| `"sm"` \| `"lg"` \| `"icon"` |
|
|
316
|
-
| `disabled` | `boolean` |
|
|
317
|
-
|
|
318
|
-
---
|
|
319
|
-
|
|
320
|
-
### ButtonGroup 按钮组
|
|
321
|
-
|
|
322
|
-
```tsx
|
|
323
|
-
import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText } from "@/components/ui/button-group"
|
|
324
|
-
import { Button } from "@/components/ui/button"
|
|
325
|
-
import { ChevronDown } from "lucide-react-taro"
|
|
326
|
-
|
|
327
|
-
{/* 水平 */}
|
|
328
|
-
<ButtonGroup>
|
|
329
|
-
<Button variant="outline">One</Button>
|
|
330
|
-
<Button variant="outline">Two</Button>
|
|
331
|
-
<Button variant="outline">Three</Button>
|
|
332
|
-
</ButtonGroup>
|
|
333
|
-
|
|
334
|
-
{/* 垂直 */}
|
|
335
|
-
<ButtonGroup orientation="vertical">
|
|
336
|
-
<Button variant="outline">One</Button>
|
|
337
|
-
<Button variant="outline">Two</Button>
|
|
338
|
-
</ButtonGroup>
|
|
339
|
-
|
|
340
|
-
{/* 带分隔符 */}
|
|
341
|
-
<ButtonGroup>
|
|
342
|
-
<Button variant="outline">Save</Button>
|
|
343
|
-
<ButtonGroupSeparator />
|
|
344
|
-
<Button variant="outline" size="icon">
|
|
345
|
-
<ChevronDown size={16} />
|
|
346
|
-
</Button>
|
|
347
|
-
</ButtonGroup>
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
### Calendar 日历
|
|
353
|
-
|
|
354
|
-
支持 `single`(单日期)和 `range`(日期范围)模式,可使用 `captionLayout="dropdown"` 启用下拉式年月选择。
|
|
355
|
-
|
|
356
|
-
```tsx
|
|
357
|
-
import { Calendar } from "@/components/ui/calendar"
|
|
358
|
-
import * as React from "react"
|
|
359
|
-
|
|
360
|
-
const [date, setDate] = React.useState<Date | undefined>(new Date())
|
|
361
|
-
|
|
362
|
-
<Calendar
|
|
363
|
-
mode="single"
|
|
364
|
-
selected={date}
|
|
365
|
-
onSelect={setDate}
|
|
366
|
-
className="rounded-lg"
|
|
367
|
-
captionLayout="dropdown"
|
|
368
|
-
/>
|
|
369
|
-
|
|
370
|
-
{/* 范围选择 */}
|
|
371
|
-
const [range, setRange] = React.useState<{ from?: Date; to?: Date }>()
|
|
372
|
-
|
|
373
|
-
<Calendar
|
|
374
|
-
mode="range"
|
|
375
|
-
selected={range}
|
|
376
|
-
onSelect={setRange}
|
|
377
|
-
className="rounded-lg"
|
|
378
|
-
/>
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
---
|
|
382
|
-
|
|
383
|
-
### Card 卡片
|
|
384
|
-
|
|
385
|
-
```tsx
|
|
386
|
-
import {
|
|
387
|
-
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
|
|
388
|
-
} from "@/components/ui/card"
|
|
389
|
-
import { Button } from "@/components/ui/button"
|
|
390
|
-
import { Input } from "@/components/ui/input"
|
|
391
|
-
import { Label } from "@/components/ui/label"
|
|
392
|
-
|
|
393
|
-
<Card className="w-full">
|
|
394
|
-
<CardHeader>
|
|
395
|
-
<CardTitle>创建项目</CardTitle>
|
|
396
|
-
<CardDescription>一键部署你的新项目。</CardDescription>
|
|
397
|
-
</CardHeader>
|
|
398
|
-
<CardContent>
|
|
399
|
-
<View className="grid w-full items-center gap-4">
|
|
400
|
-
<View className="flex flex-col gap-2">
|
|
401
|
-
<Label>名称</Label>
|
|
402
|
-
<Input placeholder="项目名称" />
|
|
403
|
-
</View>
|
|
404
|
-
</View>
|
|
405
|
-
</CardContent>
|
|
406
|
-
<CardFooter className="flex justify-between">
|
|
407
|
-
<Button variant="outline">取消</Button>
|
|
408
|
-
<Button>部署</Button>
|
|
409
|
-
</CardFooter>
|
|
410
|
-
</Card>
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
### Carousel 轮播
|
|
416
|
-
|
|
417
|
-
基于 Taro `Swiper` 组件实现。
|
|
418
|
-
|
|
419
|
-
```tsx
|
|
420
|
-
import {
|
|
421
|
-
Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious,
|
|
422
|
-
} from "@/components/ui/carousel"
|
|
423
|
-
import { Card, CardContent } from "@/components/ui/card"
|
|
424
|
-
|
|
425
|
-
<Carousel className="mx-auto w-full max-w-xs">
|
|
426
|
-
<CarouselContent className="h-48">
|
|
427
|
-
{Array.from({ length: 5 }).map((_, index) => (
|
|
428
|
-
<CarouselItem key={index}>
|
|
429
|
-
<Card className="h-full">
|
|
430
|
-
<CardContent className="flex h-full items-center justify-center p-6">
|
|
431
|
-
<Text className="text-4xl font-semibold">{index + 1}</Text>
|
|
432
|
-
</CardContent>
|
|
433
|
-
</Card>
|
|
434
|
-
</CarouselItem>
|
|
435
|
-
))}
|
|
436
|
-
</CarouselContent>
|
|
437
|
-
<CarouselPrevious />
|
|
438
|
-
<CarouselNext />
|
|
439
|
-
</Carousel>
|
|
440
|
-
|
|
441
|
-
{/* 多项显示 + 垂直 */}
|
|
442
|
-
<Carousel orientation="vertical" opts={{ displayMultipleItems: 2 }}>
|
|
443
|
-
<CarouselContent className="h-[300px]">...</CarouselContent>
|
|
444
|
-
</Carousel>
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
| 属性 | 说明 |
|
|
448
|
-
| --------------------------- | ------------------------------ |
|
|
449
|
-
| `orientation` | `"horizontal"` \| `"vertical"` |
|
|
450
|
-
| `opts.displayMultipleItems` | 同时显示项数 |
|
|
451
|
-
| `opts.loop` | 循环 |
|
|
452
|
-
| `opts.autoplay` | 自动播放 |
|
|
453
|
-
| `opts.interval` | 自动播放间隔 |
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
|
-
### Checkbox 复选框
|
|
458
|
-
|
|
459
|
-
```tsx
|
|
460
|
-
import { Checkbox } from "@/components/ui/checkbox"
|
|
461
|
-
import { Label } from "@/components/ui/label"
|
|
462
|
-
import { useState } from "react"
|
|
463
|
-
|
|
464
|
-
const [checked, setChecked] = useState(false)
|
|
465
|
-
|
|
466
|
-
<View className="flex items-center gap-2">
|
|
467
|
-
<Checkbox checked={checked} onCheckedChange={setChecked} />
|
|
468
|
-
<Label onClick={() => setChecked(!checked)}>接受条款</Label>
|
|
469
|
-
</View>
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
---
|
|
473
|
-
|
|
474
|
-
### Collapsible 可折叠
|
|
475
|
-
|
|
476
|
-
```tsx
|
|
477
|
-
import {
|
|
478
|
-
Collapsible, CollapsibleContent, CollapsibleTrigger,
|
|
479
|
-
} from "@/components/ui/collapsible"
|
|
480
|
-
import { Button } from "@/components/ui/button"
|
|
481
|
-
import { ChevronsUpDown } from "lucide-react-taro"
|
|
482
|
-
import * as React from "react"
|
|
483
|
-
|
|
484
|
-
const [isOpen, setIsOpen] = React.useState(false)
|
|
485
|
-
|
|
486
|
-
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
|
487
|
-
<View className="flex items-center justify-between">
|
|
488
|
-
<Text className="text-sm font-semibold">3 个仓库</Text>
|
|
489
|
-
<CollapsibleTrigger>
|
|
490
|
-
<Button variant="ghost" size="sm">
|
|
491
|
-
<ChevronsUpDown size={16} />
|
|
492
|
-
</Button>
|
|
493
|
-
</CollapsibleTrigger>
|
|
494
|
-
</View>
|
|
495
|
-
<View className="rounded-md border px-4 py-3">始终可见的内容</View>
|
|
496
|
-
<CollapsibleContent className="space-y-2">
|
|
497
|
-
<View className="rounded-md border px-4 py-3">可折叠内容 1</View>
|
|
498
|
-
<View className="rounded-md border px-4 py-3">可折叠内容 2</View>
|
|
499
|
-
</CollapsibleContent>
|
|
500
|
-
</Collapsible>
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
---
|
|
504
|
-
|
|
505
|
-
### Command 命令菜单
|
|
506
|
-
|
|
507
|
-
搜索/命令面板,支持分组、过滤、键盘快捷键和对话框模式。
|
|
508
|
-
|
|
509
|
-
```tsx
|
|
510
|
-
import {
|
|
511
|
-
Command, CommandDialog, CommandEmpty, CommandGroup,
|
|
512
|
-
CommandInput, CommandItem, CommandList,
|
|
513
|
-
CommandSeparator, CommandShortcut,
|
|
514
|
-
} from "@/components/ui/command"
|
|
515
|
-
import { Calendar, User } from "lucide-react-taro"
|
|
516
|
-
|
|
517
|
-
{/* 内嵌模式 */}
|
|
518
|
-
<Command className="rounded-lg border">
|
|
519
|
-
<CommandInput placeholder="搜索..." />
|
|
520
|
-
<CommandList>
|
|
521
|
-
<CommandEmpty>无结果</CommandEmpty>
|
|
522
|
-
<CommandGroup heading="建议">
|
|
523
|
-
<CommandItem>
|
|
524
|
-
<Calendar size={16} />
|
|
525
|
-
<Text>日历</Text>
|
|
526
|
-
</CommandItem>
|
|
527
|
-
</CommandGroup>
|
|
528
|
-
<CommandSeparator />
|
|
529
|
-
<CommandGroup heading="设置">
|
|
530
|
-
<CommandItem>
|
|
531
|
-
<User size={16} />
|
|
532
|
-
<Text>个人资料</Text>
|
|
533
|
-
<CommandShortcut>⌘P</CommandShortcut>
|
|
534
|
-
</CommandItem>
|
|
535
|
-
</CommandGroup>
|
|
536
|
-
</CommandList>
|
|
537
|
-
</Command>
|
|
538
|
-
|
|
539
|
-
{/* 对话框模式 */}
|
|
540
|
-
const [open, setOpen] = React.useState(false)
|
|
541
|
-
|
|
542
|
-
<Button onClick={() => setOpen(true)}>打开命令菜单</Button>
|
|
543
|
-
<CommandDialog open={open} onOpenChange={setOpen}>
|
|
544
|
-
<CommandInput placeholder="搜索..." />
|
|
545
|
-
<CommandList>...</CommandList>
|
|
546
|
-
</CommandDialog>
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
---
|
|
550
|
-
|
|
551
|
-
### ContextMenu 右键/长按菜单
|
|
552
|
-
|
|
553
|
-
H5 端通过鼠标右键触发,小程序端通过长按触发。
|
|
554
|
-
|
|
555
|
-
```tsx
|
|
556
|
-
import {
|
|
557
|
-
ContextMenu, ContextMenuCheckboxItem, ContextMenuContent,
|
|
558
|
-
ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup,
|
|
559
|
-
ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut,
|
|
560
|
-
ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent,
|
|
561
|
-
ContextMenuTrigger,
|
|
562
|
-
} from "@/components/ui/context-menu"
|
|
563
|
-
|
|
564
|
-
const [bookmarksChecked, setBookmarksChecked] = React.useState(true)
|
|
565
|
-
const [person, setPerson] = React.useState("pedro")
|
|
566
|
-
|
|
567
|
-
<ContextMenu>
|
|
568
|
-
<ContextMenuTrigger className="flex h-20 w-full items-center justify-center rounded-md border border-dashed text-sm">
|
|
569
|
-
长按此处
|
|
570
|
-
</ContextMenuTrigger>
|
|
571
|
-
<ContextMenuContent className="w-50">
|
|
572
|
-
<ContextMenuItem>
|
|
573
|
-
返回 <ContextMenuShortcut>⌘[</ContextMenuShortcut>
|
|
574
|
-
</ContextMenuItem>
|
|
575
|
-
<ContextMenuSub>
|
|
576
|
-
<ContextMenuSubTrigger>更多工具</ContextMenuSubTrigger>
|
|
577
|
-
<ContextMenuSubContent className="w-48">
|
|
578
|
-
<ContextMenuItem>保存页面</ContextMenuItem>
|
|
579
|
-
</ContextMenuSubContent>
|
|
580
|
-
</ContextMenuSub>
|
|
581
|
-
<ContextMenuSeparator />
|
|
582
|
-
<ContextMenuCheckboxItem
|
|
583
|
-
checked={bookmarksChecked}
|
|
584
|
-
onClick={() => setBookmarksChecked(!bookmarksChecked)}
|
|
585
|
-
>
|
|
586
|
-
显示书签栏
|
|
587
|
-
</ContextMenuCheckboxItem>
|
|
588
|
-
<ContextMenuRadioGroup value={person}>
|
|
589
|
-
<ContextMenuLabel>人员</ContextMenuLabel>
|
|
590
|
-
<ContextMenuRadioItem
|
|
591
|
-
value="pedro"
|
|
592
|
-
checked={person === "pedro"}
|
|
593
|
-
onClick={() => setPerson("pedro")}
|
|
594
|
-
>
|
|
595
|
-
Pedro
|
|
596
|
-
</ContextMenuRadioItem>
|
|
597
|
-
</ContextMenuRadioGroup>
|
|
598
|
-
</ContextMenuContent>
|
|
599
|
-
</ContextMenu>
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
|
-
### Dialog 对话框
|
|
605
|
-
|
|
606
|
-
点击遮罩或 X 按钮可关闭。支持受控和非受控模式。
|
|
607
|
-
|
|
608
|
-
```tsx
|
|
609
|
-
import {
|
|
610
|
-
Dialog, DialogClose, DialogContent, DialogDescription,
|
|
611
|
-
DialogFooter, DialogHeader, DialogTitle, DialogTrigger,
|
|
612
|
-
} from "@/components/ui/dialog"
|
|
613
|
-
import { Button } from "@/components/ui/button"
|
|
614
|
-
import { Input } from "@/components/ui/input"
|
|
615
|
-
import { Label } from "@/components/ui/label"
|
|
616
|
-
|
|
617
|
-
{/* 非受控 */}
|
|
618
|
-
<Dialog>
|
|
619
|
-
<DialogTrigger>
|
|
620
|
-
<Button variant="outline">编辑资料</Button>
|
|
621
|
-
</DialogTrigger>
|
|
622
|
-
<DialogContent className="sm:max-w-[425px]">
|
|
623
|
-
<DialogHeader>
|
|
624
|
-
<DialogTitle>编辑资料</DialogTitle>
|
|
625
|
-
<DialogDescription>在这里修改你的资料。</DialogDescription>
|
|
626
|
-
</DialogHeader>
|
|
627
|
-
<View className="grid gap-4 py-4">
|
|
628
|
-
<View className="grid grid-cols-4 items-center gap-4">
|
|
629
|
-
<Label className="text-right">名称</Label>
|
|
630
|
-
<Input defaultValue="Pedro" className="col-span-3" />
|
|
631
|
-
</View>
|
|
632
|
-
</View>
|
|
633
|
-
<DialogFooter>
|
|
634
|
-
<Button className="w-full">保存</Button>
|
|
635
|
-
</DialogFooter>
|
|
636
|
-
</DialogContent>
|
|
637
|
-
</Dialog>
|
|
638
|
-
|
|
639
|
-
{/* 受控 */}
|
|
640
|
-
const [open, setOpen] = React.useState(false)
|
|
641
|
-
|
|
642
|
-
<Button onClick={() => setOpen(true)}>打开</Button>
|
|
643
|
-
<Dialog open={open} onOpenChange={setOpen}>
|
|
644
|
-
<DialogContent>...</DialogContent>
|
|
645
|
-
</Dialog>
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
---
|
|
649
|
-
|
|
650
|
-
### Drawer 抽屉
|
|
651
|
-
|
|
652
|
-
从屏幕底部滑出的面板。
|
|
653
|
-
|
|
654
|
-
```tsx
|
|
655
|
-
import {
|
|
656
|
-
Drawer, DrawerClose, DrawerContent, DrawerDescription,
|
|
657
|
-
DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger,
|
|
658
|
-
} from "@/components/ui/drawer"
|
|
659
|
-
import { Button } from "@/components/ui/button"
|
|
660
|
-
import { Minus, Plus } from "lucide-react-taro"
|
|
661
|
-
|
|
662
|
-
const [goal, setGoal] = React.useState(350)
|
|
663
|
-
|
|
664
|
-
<Drawer>
|
|
665
|
-
<DrawerTrigger>
|
|
666
|
-
<Button variant="outline">打开抽屉</Button>
|
|
667
|
-
</DrawerTrigger>
|
|
668
|
-
<DrawerContent>
|
|
669
|
-
<View className="mx-auto w-full max-w-sm">
|
|
670
|
-
<DrawerHeader>
|
|
671
|
-
<DrawerTitle>每日目标</DrawerTitle>
|
|
672
|
-
<DrawerDescription>设置你的每日活动目标。</DrawerDescription>
|
|
673
|
-
</DrawerHeader>
|
|
674
|
-
<View className="p-4">
|
|
675
|
-
<View className="flex items-center justify-center space-x-2">
|
|
676
|
-
<Button variant="outline" size="icon" onClick={() => setGoal(goal - 10)}>
|
|
677
|
-
<Minus size={16} />
|
|
678
|
-
</Button>
|
|
679
|
-
<View className="text-7xl font-bold">{goal}</View>
|
|
680
|
-
<Button variant="outline" size="icon" onClick={() => setGoal(goal + 10)}>
|
|
681
|
-
<Plus size={16} />
|
|
682
|
-
</Button>
|
|
683
|
-
</View>
|
|
684
|
-
</View>
|
|
685
|
-
<DrawerFooter>
|
|
686
|
-
<Button>提交</Button>
|
|
687
|
-
<DrawerClose>
|
|
688
|
-
<Button variant="outline">取消</Button>
|
|
689
|
-
</DrawerClose>
|
|
690
|
-
</DrawerFooter>
|
|
691
|
-
</View>
|
|
692
|
-
</DrawerContent>
|
|
693
|
-
</Drawer>
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
---
|
|
697
|
-
|
|
698
|
-
### DropdownMenu 下拉菜单
|
|
699
|
-
|
|
700
|
-
支持子菜单、CheckboxItem、RadioItem。
|
|
701
|
-
|
|
702
|
-
```tsx
|
|
703
|
-
import {
|
|
704
|
-
DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent,
|
|
705
|
-
DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel,
|
|
706
|
-
DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub,
|
|
707
|
-
DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger,
|
|
708
|
-
DropdownMenuPortal,
|
|
709
|
-
} from "@/components/ui/dropdown-menu"
|
|
710
|
-
import { Button } from "@/components/ui/button"
|
|
711
|
-
|
|
712
|
-
<DropdownMenu>
|
|
713
|
-
<DropdownMenuTrigger>
|
|
714
|
-
<Button variant="outline">打开菜单</Button>
|
|
715
|
-
</DropdownMenuTrigger>
|
|
716
|
-
<DropdownMenuContent className="w-44">
|
|
717
|
-
<DropdownMenuLabel>我的账户</DropdownMenuLabel>
|
|
718
|
-
<DropdownMenuGroup>
|
|
719
|
-
<DropdownMenuItem>
|
|
720
|
-
<Text>个人资料</Text>
|
|
721
|
-
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
722
|
-
</DropdownMenuItem>
|
|
723
|
-
</DropdownMenuGroup>
|
|
724
|
-
<DropdownMenuSeparator />
|
|
725
|
-
<DropdownMenuSub>
|
|
726
|
-
<DropdownMenuSubTrigger>
|
|
727
|
-
<Text>邀请用户</Text>
|
|
728
|
-
</DropdownMenuSubTrigger>
|
|
729
|
-
<DropdownMenuPortal>
|
|
730
|
-
<DropdownMenuSubContent className="w-40">
|
|
731
|
-
<DropdownMenuItem><Text>邮件</Text></DropdownMenuItem>
|
|
732
|
-
<DropdownMenuItem><Text>消息</Text></DropdownMenuItem>
|
|
733
|
-
</DropdownMenuSubContent>
|
|
734
|
-
</DropdownMenuPortal>
|
|
735
|
-
</DropdownMenuSub>
|
|
736
|
-
</DropdownMenuContent>
|
|
737
|
-
</DropdownMenu>
|
|
738
|
-
```
|
|
739
|
-
|
|
740
|
-
---
|
|
741
|
-
|
|
742
|
-
### Field 表单字段组
|
|
743
|
-
|
|
744
|
-
结构化表单布局组件,支持水平/垂直方向。
|
|
745
|
-
|
|
746
|
-
```tsx
|
|
747
|
-
import {
|
|
748
|
-
Field, FieldDescription, FieldGroup, FieldLabel,
|
|
749
|
-
FieldLegend, FieldSeparator, FieldSet,
|
|
750
|
-
} from "@/components/ui/field"
|
|
751
|
-
import { Input } from "@/components/ui/input"
|
|
752
|
-
import { Checkbox } from "@/components/ui/checkbox"
|
|
753
|
-
|
|
754
|
-
<FieldGroup>
|
|
755
|
-
<FieldSet>
|
|
756
|
-
<FieldLegend>支付方式</FieldLegend>
|
|
757
|
-
<FieldDescription>所有交易安全加密</FieldDescription>
|
|
758
|
-
<FieldGroup>
|
|
759
|
-
<Field>
|
|
760
|
-
<FieldLabel>持卡人姓名</FieldLabel>
|
|
761
|
-
<Input placeholder="张三" />
|
|
762
|
-
</Field>
|
|
763
|
-
<Field>
|
|
764
|
-
<FieldLabel>卡号</FieldLabel>
|
|
765
|
-
<Input placeholder="1234 5678 9012 3456" />
|
|
766
|
-
<FieldDescription>请输入 16 位卡号</FieldDescription>
|
|
767
|
-
</Field>
|
|
768
|
-
</FieldGroup>
|
|
769
|
-
</FieldSet>
|
|
770
|
-
<FieldSeparator />
|
|
771
|
-
<FieldSet>
|
|
772
|
-
<FieldLegend>账单地址</FieldLegend>
|
|
773
|
-
<FieldGroup>
|
|
774
|
-
<Field orientation="horizontal">
|
|
775
|
-
<Checkbox />
|
|
776
|
-
<FieldLabel className="font-normal">与收货地址相同</FieldLabel>
|
|
777
|
-
</Field>
|
|
778
|
-
</FieldGroup>
|
|
779
|
-
</FieldSet>
|
|
780
|
-
</FieldGroup>
|
|
781
|
-
```
|
|
782
|
-
|
|
783
|
-
---
|
|
784
|
-
|
|
785
|
-
### HoverCard 悬停卡片
|
|
786
|
-
|
|
787
|
-
H5 端鼠标 hover 触发,小程序端点击触发。
|
|
788
|
-
|
|
789
|
-
```tsx
|
|
790
|
-
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
|
|
791
|
-
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
792
|
-
import { Button } from "@/components/ui/button"
|
|
793
|
-
|
|
794
|
-
<HoverCard>
|
|
795
|
-
<HoverCardTrigger className="inline-block">
|
|
796
|
-
<Button variant="link">@nextjs</Button>
|
|
797
|
-
</HoverCardTrigger>
|
|
798
|
-
<HoverCardContent className="w-80" side="top" align="start">
|
|
799
|
-
<View className="flex justify-between space-x-4">
|
|
800
|
-
<Avatar>
|
|
801
|
-
<AvatarImage src="https://github.com/vercel.png" />
|
|
802
|
-
<AvatarFallback>VC</AvatarFallback>
|
|
803
|
-
</Avatar>
|
|
804
|
-
<View className="space-y-1">
|
|
805
|
-
<View className="text-sm font-semibold">@nextjs</View>
|
|
806
|
-
<View className="text-sm">The React Framework</View>
|
|
807
|
-
</View>
|
|
808
|
-
</View>
|
|
809
|
-
</HoverCardContent>
|
|
810
|
-
</HoverCard>
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
| 属性 | 值 |
|
|
814
|
-
| ----------------------- | ---------------------------------------------- |
|
|
815
|
-
| `side` | `"top"` \| `"right"` \| `"bottom"` \| `"left"` |
|
|
816
|
-
| `align` | `"start"` \| `"center"` \| `"end"` |
|
|
817
|
-
| `open` / `onOpenChange` | 受控模式 |
|
|
818
|
-
|
|
819
|
-
---
|
|
820
|
-
|
|
821
|
-
### Input 输入框
|
|
822
|
-
|
|
823
|
-
外层 `View` 处理聚焦样式,内层使用 Taro `Input` 组件。
|
|
824
|
-
|
|
825
|
-
```tsx
|
|
826
|
-
import { Input } from "@/components/ui/input"
|
|
827
|
-
import { Label } from "@/components/ui/label"
|
|
828
|
-
|
|
829
|
-
<View className="flex w-full max-w-sm flex-col gap-2">
|
|
830
|
-
<Label>邮箱</Label>
|
|
831
|
-
<Input type="text" placeholder="请输入邮箱" />
|
|
832
|
-
</View>
|
|
833
|
-
|
|
834
|
-
{/* 密码 */}
|
|
835
|
-
<Input type="text" password placeholder="密码" />
|
|
836
|
-
|
|
837
|
-
{/* 禁用 */}
|
|
838
|
-
<Input disabled placeholder="已禁用" />
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
---
|
|
842
|
-
|
|
843
|
-
### InputGroup 输入框组
|
|
844
|
-
|
|
845
|
-
```tsx
|
|
846
|
-
import {
|
|
847
|
-
InputGroup, InputGroupAddon, InputGroupButton,
|
|
848
|
-
InputGroupInput, InputGroupText, InputGroupTextarea,
|
|
849
|
-
} from "@/components/ui/input-group"
|
|
850
|
-
import { Search, Copy, FileCode } from "lucide-react-taro"
|
|
851
|
-
|
|
852
|
-
{/* 前置 Addon */}
|
|
853
|
-
<InputGroup>
|
|
854
|
-
<InputGroupAddon>@</InputGroupAddon>
|
|
855
|
-
<InputGroupInput placeholder="用户名" />
|
|
856
|
-
</InputGroup>
|
|
857
|
-
|
|
858
|
-
{/* 后置搜索图标 */}
|
|
859
|
-
<InputGroup>
|
|
860
|
-
<InputGroupInput placeholder="搜索..." />
|
|
861
|
-
<InputGroupAddon align="inline-end">
|
|
862
|
-
<Search color="#737373" size={16} />
|
|
863
|
-
</InputGroupAddon>
|
|
864
|
-
</InputGroup>
|
|
865
|
-
|
|
866
|
-
{/* 带按钮 */}
|
|
867
|
-
<InputGroup>
|
|
868
|
-
<InputGroupInput placeholder="邮箱" />
|
|
869
|
-
<InputGroupButton>订阅</InputGroupButton>
|
|
870
|
-
</InputGroup>
|
|
871
|
-
|
|
872
|
-
{/* Textarea 模式 */}
|
|
873
|
-
<InputGroup>
|
|
874
|
-
<InputGroupTextarea placeholder="代码..." className="font-mono text-sm h-40" />
|
|
875
|
-
<InputGroupAddon align="block-start">
|
|
876
|
-
<FileCode color="#737373" size={16} />
|
|
877
|
-
<InputGroupText className="font-mono">script.js</InputGroupText>
|
|
878
|
-
<InputGroupButton size="icon-xs" className="ml-auto">
|
|
879
|
-
<Copy color="#737373" size={16} />
|
|
880
|
-
</InputGroupButton>
|
|
881
|
-
</InputGroupAddon>
|
|
882
|
-
</InputGroup>
|
|
883
|
-
```
|
|
884
|
-
|
|
885
|
-
---
|
|
886
|
-
|
|
887
|
-
### InputOTP 一次性密码输入
|
|
888
|
-
|
|
889
|
-
```tsx
|
|
890
|
-
import {
|
|
891
|
-
InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot,
|
|
892
|
-
} from "@/components/ui/input-otp"
|
|
893
|
-
|
|
894
|
-
const [value, setValue] = React.useState("")
|
|
895
|
-
|
|
896
|
-
{/* 6 位带分隔符 */}
|
|
897
|
-
<InputOTP maxLength={6}>
|
|
898
|
-
<InputOTPGroup>
|
|
899
|
-
<InputOTPSlot index={0} />
|
|
900
|
-
<InputOTPSlot index={1} />
|
|
901
|
-
<InputOTPSlot index={2} />
|
|
902
|
-
</InputOTPGroup>
|
|
903
|
-
<InputOTPSeparator />
|
|
904
|
-
<InputOTPGroup>
|
|
905
|
-
<InputOTPSlot index={3} />
|
|
906
|
-
<InputOTPSlot index={4} />
|
|
907
|
-
<InputOTPSlot index={5} />
|
|
908
|
-
</InputOTPGroup>
|
|
909
|
-
</InputOTP>
|
|
910
|
-
|
|
911
|
-
{/* 受控 */}
|
|
912
|
-
<InputOTP maxLength={4} value={value} onChange={setValue}>
|
|
913
|
-
<InputOTPGroup>
|
|
914
|
-
<InputOTPSlot index={0} />
|
|
915
|
-
<InputOTPSlot index={1} />
|
|
916
|
-
<InputOTPSlot index={2} />
|
|
917
|
-
<InputOTPSlot index={3} />
|
|
918
|
-
</InputOTPGroup>
|
|
919
|
-
</InputOTP>
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
### Label 标签
|
|
925
|
-
|
|
926
|
-
```tsx
|
|
927
|
-
import { Label } from "@/components/ui/label"
|
|
928
|
-
import { Input } from "@/components/ui/input"
|
|
929
|
-
|
|
930
|
-
<View className="grid w-full max-w-sm items-center gap-2">
|
|
931
|
-
<Label>邮箱</Label>
|
|
932
|
-
<Input type="text" placeholder="请输入邮箱" />
|
|
933
|
-
</View>
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
---
|
|
937
|
-
|
|
938
|
-
### Menubar 菜单栏
|
|
939
|
-
|
|
940
|
-
```tsx
|
|
941
|
-
import {
|
|
942
|
-
Menubar, MenubarCheckboxItem, MenubarContent, MenubarItem,
|
|
943
|
-
MenubarMenu, MenubarRadioGroup, MenubarRadioItem,
|
|
944
|
-
MenubarSeparator, MenubarShortcut, MenubarSub,
|
|
945
|
-
MenubarSubContent, MenubarSubTrigger, MenubarTrigger,
|
|
946
|
-
} from "@/components/ui/menubar"
|
|
947
|
-
|
|
948
|
-
<Menubar>
|
|
949
|
-
<MenubarMenu>
|
|
950
|
-
<MenubarTrigger>文件</MenubarTrigger>
|
|
951
|
-
<MenubarContent>
|
|
952
|
-
<MenubarItem>
|
|
953
|
-
新标签页 <MenubarShortcut>⌘T</MenubarShortcut>
|
|
954
|
-
</MenubarItem>
|
|
955
|
-
<MenubarSeparator />
|
|
956
|
-
<MenubarSub>
|
|
957
|
-
<MenubarSubTrigger>分享</MenubarSubTrigger>
|
|
958
|
-
<MenubarSubContent>
|
|
959
|
-
<MenubarItem>邮件</MenubarItem>
|
|
960
|
-
<MenubarItem>消息</MenubarItem>
|
|
961
|
-
</MenubarSubContent>
|
|
962
|
-
</MenubarSub>
|
|
963
|
-
</MenubarContent>
|
|
964
|
-
</MenubarMenu>
|
|
965
|
-
<MenubarMenu>
|
|
966
|
-
<MenubarTrigger>视图</MenubarTrigger>
|
|
967
|
-
<MenubarContent>
|
|
968
|
-
<MenubarCheckboxItem checked>显示完整 URL</MenubarCheckboxItem>
|
|
969
|
-
</MenubarContent>
|
|
970
|
-
</MenubarMenu>
|
|
971
|
-
</Menubar>
|
|
972
|
-
```
|
|
973
|
-
|
|
974
|
-
---
|
|
975
|
-
|
|
976
|
-
### NavigationMenu 导航菜单
|
|
977
|
-
|
|
978
|
-
```tsx
|
|
979
|
-
import {
|
|
980
|
-
NavigationMenu, NavigationMenuContent, NavigationMenuItem,
|
|
981
|
-
NavigationMenuLink, NavigationMenuList,
|
|
982
|
-
NavigationMenuTrigger, navigationMenuTriggerStyle,
|
|
983
|
-
} from "@/components/ui/navigation-menu"
|
|
984
|
-
import Taro from "@tarojs/taro"
|
|
985
|
-
import { cn } from "@/lib/utils"
|
|
986
|
-
|
|
987
|
-
<NavigationMenu className="w-full justify-start">
|
|
988
|
-
<NavigationMenuList className="flex-wrap gap-1 space-x-0">
|
|
989
|
-
<NavigationMenuItem>
|
|
990
|
-
<NavigationMenuTrigger>快速开始</NavigationMenuTrigger>
|
|
991
|
-
<NavigationMenuContent className="p-2">
|
|
992
|
-
<View className="w-96 space-y-2">
|
|
993
|
-
<NavigationMenuLink
|
|
994
|
-
className="block rounded-md p-3 hover:bg-accent"
|
|
995
|
-
onClick={() => Taro.navigateTo({ url: "/pages/intro/index" })}
|
|
996
|
-
>
|
|
997
|
-
<View className="text-sm font-medium">介绍</View>
|
|
998
|
-
<Text className="text-sm text-muted-foreground">
|
|
999
|
-
基于 Tailwind CSS 的可复用组件。
|
|
1000
|
-
</Text>
|
|
1001
|
-
</NavigationMenuLink>
|
|
1002
|
-
</View>
|
|
1003
|
-
</NavigationMenuContent>
|
|
1004
|
-
</NavigationMenuItem>
|
|
1005
|
-
<NavigationMenuItem>
|
|
1006
|
-
<NavigationMenuLink
|
|
1007
|
-
className={navigationMenuTriggerStyle()}
|
|
1008
|
-
onClick={() => Taro.navigateTo({ url: "/pages/intro/index" })}
|
|
1009
|
-
>
|
|
1010
|
-
文档
|
|
1011
|
-
</NavigationMenuLink>
|
|
1012
|
-
</NavigationMenuItem>
|
|
1013
|
-
</NavigationMenuList>
|
|
1014
|
-
</NavigationMenu>
|
|
1015
|
-
```
|
|
1016
|
-
|
|
1017
|
-
---
|
|
1018
|
-
|
|
1019
|
-
### Pagination 分页
|
|
1020
|
-
|
|
1021
|
-
```tsx
|
|
1022
|
-
import {
|
|
1023
|
-
Pagination, PaginationContent, PaginationEllipsis,
|
|
1024
|
-
PaginationItem, PaginationLink, PaginationNext, PaginationPrevious,
|
|
1025
|
-
} from "@/components/ui/pagination"
|
|
1026
|
-
|
|
1027
|
-
<Pagination>
|
|
1028
|
-
<PaginationContent>
|
|
1029
|
-
<PaginationItem><PaginationPrevious /></PaginationItem>
|
|
1030
|
-
<PaginationItem><PaginationLink>1</PaginationLink></PaginationItem>
|
|
1031
|
-
<PaginationItem><PaginationLink isActive>2</PaginationLink></PaginationItem>
|
|
1032
|
-
<PaginationItem><PaginationLink>3</PaginationLink></PaginationItem>
|
|
1033
|
-
<PaginationItem><PaginationEllipsis /></PaginationItem>
|
|
1034
|
-
<PaginationItem><PaginationNext /></PaginationItem>
|
|
1035
|
-
</PaginationContent>
|
|
1036
|
-
</Pagination>
|
|
1037
|
-
```
|
|
1038
|
-
|
|
1039
|
-
---
|
|
1040
|
-
|
|
1041
|
-
### Popover 弹出框
|
|
1042
|
-
|
|
1043
|
-
```tsx
|
|
1044
|
-
import {
|
|
1045
|
-
Popover, PopoverContent, PopoverDescription,
|
|
1046
|
-
PopoverHeader, PopoverTitle, PopoverTrigger,
|
|
1047
|
-
} from "@/components/ui/popover"
|
|
1048
|
-
import { Button } from "@/components/ui/button"
|
|
1049
|
-
import { Input } from "@/components/ui/input"
|
|
1050
|
-
import { Label } from "@/components/ui/label"
|
|
1051
|
-
|
|
1052
|
-
<Popover>
|
|
1053
|
-
<PopoverTrigger>
|
|
1054
|
-
<Button variant="outline">打开设置</Button>
|
|
1055
|
-
</PopoverTrigger>
|
|
1056
|
-
<PopoverContent className="w-80">
|
|
1057
|
-
<PopoverHeader>
|
|
1058
|
-
<PopoverTitle>尺寸设置</PopoverTitle>
|
|
1059
|
-
<PopoverDescription>设置图层的尺寸。</PopoverDescription>
|
|
1060
|
-
</PopoverHeader>
|
|
1061
|
-
<View className="grid gap-2">
|
|
1062
|
-
<View className="grid grid-cols-3 items-center gap-4">
|
|
1063
|
-
<Label>宽度</Label>
|
|
1064
|
-
<Input defaultValue="100%" className="col-span-2 h-8" />
|
|
1065
|
-
</View>
|
|
1066
|
-
</View>
|
|
1067
|
-
</PopoverContent>
|
|
1068
|
-
</Popover>
|
|
1069
|
-
```
|
|
1070
|
-
|
|
1071
|
-
| 属性 | 值 |
|
|
1072
|
-
| ------------------- | ---------------------------------------------- |
|
|
1073
|
-
| `position` / `side` | `"top"` \| `"bottom"` \| `"left"` \| `"right"` |
|
|
1074
|
-
| `align` | `"start"` \| `"center"` \| `"end"` |
|
|
1075
|
-
|
|
1076
|
-
---
|
|
1077
|
-
|
|
1078
|
-
### Progress 进度条
|
|
1079
|
-
|
|
1080
|
-
```tsx
|
|
1081
|
-
import { Progress } from "@/components/ui/progress"
|
|
1082
|
-
|
|
1083
|
-
const [progress, setProgress] = React.useState(13)
|
|
1084
|
-
React.useEffect(() => {
|
|
1085
|
-
const timer = setTimeout(() => setProgress(66), 500)
|
|
1086
|
-
return () => clearTimeout(timer)
|
|
1087
|
-
}, [])
|
|
1088
|
-
|
|
1089
|
-
<Progress value={progress} />
|
|
1090
|
-
<Progress value={66} />
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1093
|
-
---
|
|
1094
|
-
|
|
1095
|
-
### RadioGroup 单选组
|
|
1096
|
-
|
|
1097
|
-
```tsx
|
|
1098
|
-
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
|
1099
|
-
import { Label } from "@/components/ui/label"
|
|
1100
|
-
|
|
1101
|
-
<RadioGroup defaultValue="comfortable">
|
|
1102
|
-
<View className="flex items-center gap-2">
|
|
1103
|
-
<RadioGroupItem value="default" />
|
|
1104
|
-
<Label>默认</Label>
|
|
1105
|
-
</View>
|
|
1106
|
-
<View className="flex items-center gap-2">
|
|
1107
|
-
<RadioGroupItem value="comfortable" />
|
|
1108
|
-
<Label>舒适</Label>
|
|
1109
|
-
</View>
|
|
1110
|
-
<View className="flex items-center gap-2">
|
|
1111
|
-
<RadioGroupItem value="compact" />
|
|
1112
|
-
<Label>紧凑</Label>
|
|
1113
|
-
</View>
|
|
1114
|
-
</RadioGroup>
|
|
1115
|
-
```
|
|
1116
|
-
|
|
1117
|
-
---
|
|
1118
|
-
|
|
1119
|
-
### Resizable 可调整大小
|
|
1120
|
-
|
|
1121
|
-
支持水平、垂直、嵌套布局。
|
|
1122
|
-
|
|
1123
|
-
```tsx
|
|
1124
|
-
import {
|
|
1125
|
-
ResizableHandle, ResizablePanel, ResizablePanelGroup,
|
|
1126
|
-
} from "@/components/ui/resizable"
|
|
1127
|
-
|
|
1128
|
-
{/* 水平 */}
|
|
1129
|
-
<ResizablePanelGroup direction="horizontal" className="min-h-30 rounded-lg border">
|
|
1130
|
-
<ResizablePanel defaultSize={50}>
|
|
1131
|
-
<View className="flex items-center justify-center p-6">面板一</View>
|
|
1132
|
-
</ResizablePanel>
|
|
1133
|
-
<ResizableHandle withHandle />
|
|
1134
|
-
<ResizablePanel defaultSize={50}>
|
|
1135
|
-
<View className="flex items-center justify-center p-6">面板二</View>
|
|
1136
|
-
</ResizablePanel>
|
|
1137
|
-
</ResizablePanelGroup>
|
|
1138
|
-
|
|
1139
|
-
{/* 嵌套 */}
|
|
1140
|
-
<ResizablePanelGroup direction="horizontal" className="min-h-45 rounded-lg border">
|
|
1141
|
-
<ResizablePanel defaultSize={20}>
|
|
1142
|
-
<View className="p-6">侧边栏</View>
|
|
1143
|
-
</ResizablePanel>
|
|
1144
|
-
<ResizableHandle withHandle />
|
|
1145
|
-
<ResizablePanel defaultSize={80}>
|
|
1146
|
-
<ResizablePanelGroup direction="vertical">
|
|
1147
|
-
<ResizablePanel defaultSize={50}>
|
|
1148
|
-
<View className="p-6">头部</View>
|
|
1149
|
-
</ResizablePanel>
|
|
1150
|
-
<ResizableHandle withHandle />
|
|
1151
|
-
<ResizablePanel defaultSize={50}>
|
|
1152
|
-
<View className="p-6">内容</View>
|
|
1153
|
-
</ResizablePanel>
|
|
1154
|
-
</ResizablePanelGroup>
|
|
1155
|
-
</ResizablePanel>
|
|
1156
|
-
</ResizablePanelGroup>
|
|
1157
|
-
```
|
|
1158
|
-
|
|
1159
|
-
---
|
|
1160
|
-
|
|
1161
|
-
### ScrollArea 滚动区域
|
|
1162
|
-
|
|
1163
|
-
```tsx
|
|
1164
|
-
import { ScrollArea } from "@/components/ui/scroll-area"
|
|
1165
|
-
import { Separator } from "@/components/ui/separator"
|
|
1166
|
-
|
|
1167
|
-
const tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`)
|
|
1168
|
-
|
|
1169
|
-
<ScrollArea className="h-72 w-full rounded-md border">
|
|
1170
|
-
<View className="p-4">
|
|
1171
|
-
<Text className="mb-4 text-sm font-medium">Tags</Text>
|
|
1172
|
-
{tags.map((tag) => (
|
|
1173
|
-
<View key={tag}>
|
|
1174
|
-
<View className="text-sm">{tag}</View>
|
|
1175
|
-
<Separator className="my-2" />
|
|
1176
|
-
</View>
|
|
1177
|
-
))}
|
|
1178
|
-
</View>
|
|
1179
|
-
</ScrollArea>
|
|
1180
|
-
```
|
|
1181
|
-
|
|
1182
|
-
---
|
|
1183
|
-
|
|
1184
|
-
### Select 选择器
|
|
1185
|
-
|
|
1186
|
-
```tsx
|
|
1187
|
-
import {
|
|
1188
|
-
Select, SelectContent, SelectGroup, SelectItem,
|
|
1189
|
-
SelectLabel, SelectTrigger, SelectValue,
|
|
1190
|
-
} from "@/components/ui/select"
|
|
1191
|
-
|
|
1192
|
-
<Select>
|
|
1193
|
-
<SelectTrigger className="w-full">
|
|
1194
|
-
<SelectValue placeholder="选择水果" />
|
|
1195
|
-
</SelectTrigger>
|
|
1196
|
-
<SelectContent>
|
|
1197
|
-
<SelectGroup>
|
|
1198
|
-
<SelectLabel>水果</SelectLabel>
|
|
1199
|
-
<SelectItem value="apple">苹果</SelectItem>
|
|
1200
|
-
<SelectItem value="banana">香蕉</SelectItem>
|
|
1201
|
-
<SelectItem value="blueberry">蓝莓</SelectItem>
|
|
1202
|
-
</SelectGroup>
|
|
1203
|
-
</SelectContent>
|
|
1204
|
-
</Select>
|
|
1205
|
-
|
|
1206
|
-
{/* 受控 + 多分组 */}
|
|
1207
|
-
const [value, setValue] = React.useState("")
|
|
1208
|
-
|
|
1209
|
-
<Select value={value} onValueChange={setValue}>
|
|
1210
|
-
<SelectTrigger className="w-full">
|
|
1211
|
-
<SelectValue placeholder="选择时区" />
|
|
1212
|
-
</SelectTrigger>
|
|
1213
|
-
<SelectContent>
|
|
1214
|
-
<SelectGroup>
|
|
1215
|
-
<SelectLabel>北美</SelectLabel>
|
|
1216
|
-
<SelectItem value="est">东部标准时间</SelectItem>
|
|
1217
|
-
<SelectItem value="cst">中部标准时间</SelectItem>
|
|
1218
|
-
</SelectGroup>
|
|
1219
|
-
<SelectGroup>
|
|
1220
|
-
<SelectLabel>欧洲</SelectLabel>
|
|
1221
|
-
<SelectItem value="gmt">格林威治时间</SelectItem>
|
|
1222
|
-
</SelectGroup>
|
|
1223
|
-
</SelectContent>
|
|
1224
|
-
</Select>
|
|
1225
|
-
```
|
|
1226
|
-
|
|
1227
|
-
---
|
|
1228
|
-
|
|
1229
|
-
### Separator 分隔符
|
|
1230
|
-
|
|
1231
|
-
```tsx
|
|
1232
|
-
import { Separator } from "@/components/ui/separator"
|
|
1233
|
-
|
|
1234
|
-
{/* 水平 */}
|
|
1235
|
-
<Separator className="my-4" />
|
|
1236
|
-
|
|
1237
|
-
{/* 垂直 */}
|
|
1238
|
-
<View className="flex h-5 items-center space-x-4 text-sm">
|
|
1239
|
-
<Text>博客</Text>
|
|
1240
|
-
<Separator orientation="vertical" />
|
|
1241
|
-
<Text>文档</Text>
|
|
1242
|
-
<Separator orientation="vertical" />
|
|
1243
|
-
<Text>源码</Text>
|
|
1244
|
-
</View>
|
|
1245
|
-
```
|
|
1246
|
-
|
|
1247
|
-
---
|
|
1248
|
-
|
|
1249
|
-
### Sheet 侧边栏面板
|
|
1250
|
-
|
|
1251
|
-
支持上下左右四个方向滑出。
|
|
1252
|
-
|
|
1253
|
-
```tsx
|
|
1254
|
-
import {
|
|
1255
|
-
Sheet, SheetClose, SheetContent, SheetDescription,
|
|
1256
|
-
SheetFooter, SheetHeader, SheetTitle, SheetTrigger,
|
|
1257
|
-
} from "@/components/ui/sheet"
|
|
1258
|
-
import { Button } from "@/components/ui/button"
|
|
1259
|
-
|
|
1260
|
-
<Sheet>
|
|
1261
|
-
<SheetTrigger>
|
|
1262
|
-
<Button variant="outline">右侧面板</Button>
|
|
1263
|
-
</SheetTrigger>
|
|
1264
|
-
<SheetContent side="right">
|
|
1265
|
-
<SheetHeader>
|
|
1266
|
-
<SheetTitle>编辑资料</SheetTitle>
|
|
1267
|
-
<SheetDescription>修改你的个人信息。</SheetDescription>
|
|
1268
|
-
</SheetHeader>
|
|
1269
|
-
<View className="grid gap-4 py-4">
|
|
1270
|
-
<Label>名称</Label>
|
|
1271
|
-
<Input defaultValue="Pedro" />
|
|
1272
|
-
</View>
|
|
1273
|
-
<SheetFooter>
|
|
1274
|
-
<SheetClose>
|
|
1275
|
-
<Button>保存</Button>
|
|
1276
|
-
</SheetClose>
|
|
1277
|
-
</SheetFooter>
|
|
1278
|
-
</SheetContent>
|
|
1279
|
-
</Sheet>
|
|
1280
|
-
```
|
|
1281
|
-
|
|
1282
|
-
| `side` | 效果 |
|
|
1283
|
-
| ---------- | ---------- |
|
|
1284
|
-
| `"top"` | 从顶部滑出 |
|
|
1285
|
-
| `"right"` | 从右侧滑出 |
|
|
1286
|
-
| `"bottom"` | 从底部滑出 |
|
|
1287
|
-
| `"left"` | 从左侧滑出 |
|
|
1288
|
-
|
|
1289
|
-
---
|
|
1290
|
-
|
|
1291
|
-
### Skeleton 骨架屏
|
|
1292
|
-
|
|
1293
|
-
```tsx
|
|
1294
|
-
import { Skeleton } from "@/components/ui/skeleton"
|
|
1295
|
-
|
|
1296
|
-
{/* 个人资料加载态 */}
|
|
1297
|
-
<View className="flex items-center space-x-4">
|
|
1298
|
-
<Skeleton className="h-12 w-12 rounded-full" />
|
|
1299
|
-
<View className="space-y-2">
|
|
1300
|
-
<Skeleton className="h-4 w-30" />
|
|
1301
|
-
<Skeleton className="h-4 w-40" />
|
|
1302
|
-
</View>
|
|
1303
|
-
</View>
|
|
1304
|
-
|
|
1305
|
-
{/* 卡片加载态 */}
|
|
1306
|
-
<View className="flex flex-col space-y-3">
|
|
1307
|
-
<Skeleton className="h-40 w-full rounded-xl" />
|
|
1308
|
-
<Skeleton className="h-4 w-full" />
|
|
1309
|
-
<Skeleton className="h-4 w-full" />
|
|
1310
|
-
</View>
|
|
1311
|
-
```
|
|
1312
|
-
|
|
1313
|
-
---
|
|
1314
|
-
|
|
1315
|
-
### Slider 滑块
|
|
1316
|
-
|
|
1317
|
-
```tsx
|
|
1318
|
-
import { Slider } from "@/components/ui/slider"
|
|
1319
|
-
|
|
1320
|
-
{/* 基础 */}
|
|
1321
|
-
<Slider defaultValue={[50]} max={100} step={1} className="w-[60%]" />
|
|
1322
|
-
|
|
1323
|
-
{/* 自定义颜色 */}
|
|
1324
|
-
<Slider
|
|
1325
|
-
defaultValue={[50]}
|
|
1326
|
-
max={100}
|
|
1327
|
-
step={1}
|
|
1328
|
-
rangeClassName="bg-green-500"
|
|
1329
|
-
thumbClassName="border-green-500 focus:ring-green-500 focus:ring-opacity-30"
|
|
1330
|
-
/>
|
|
1331
|
-
|
|
1332
|
-
{/* 垂直 */}
|
|
1333
|
-
<View className="h-[200px]">
|
|
1334
|
-
<Slider defaultValue={[50]} max={100} step={1} orientation="vertical" />
|
|
1335
|
-
</View>
|
|
1336
|
-
```
|
|
1337
|
-
|
|
1338
|
-
| 属性 | 说明 |
|
|
1339
|
-
| ------------------------- | ------------------------------ |
|
|
1340
|
-
| `defaultValue` | `number[]` 初始值 |
|
|
1341
|
-
| `value` / `onValueChange` | 受控模式 |
|
|
1342
|
-
| `min` / `max` / `step` | 范围和步长 |
|
|
1343
|
-
| `orientation` | `"horizontal"` \| `"vertical"` |
|
|
1344
|
-
| `rangeClassName` | 已选范围样式 |
|
|
1345
|
-
| `thumbClassName` | 滑块手柄样式 |
|
|
1346
|
-
|
|
1347
|
-
---
|
|
1348
|
-
|
|
1349
|
-
### Switch 开关
|
|
1350
|
-
|
|
1351
|
-
```tsx
|
|
1352
|
-
import { Switch } from "@/components/ui/switch"
|
|
1353
|
-
import { Label } from "@/components/ui/label"
|
|
1354
|
-
|
|
1355
|
-
<View className="flex items-center gap-2">
|
|
1356
|
-
<Switch />
|
|
1357
|
-
<Label>飞行模式</Label>
|
|
1358
|
-
</View>
|
|
1359
|
-
|
|
1360
|
-
{/* 自定义颜色 */}
|
|
1361
|
-
<Switch defaultChecked className="data-[state=checked]:bg-green-500" />
|
|
1362
|
-
|
|
1363
|
-
{/* 禁用 */}
|
|
1364
|
-
<Switch disabled />
|
|
1365
|
-
<Switch disabled defaultChecked />
|
|
1366
|
-
```
|
|
1367
|
-
|
|
1368
|
-
---
|
|
1369
|
-
|
|
1370
|
-
### Table 表格
|
|
1371
|
-
|
|
1372
|
-
```tsx
|
|
1373
|
-
import {
|
|
1374
|
-
Table, TableBody, TableCaption, TableCell,
|
|
1375
|
-
TableFooter, TableHead, TableHeader, TableRow,
|
|
1376
|
-
} from "@/components/ui/table"
|
|
1377
|
-
|
|
1378
|
-
const invoices = [
|
|
1379
|
-
{ invoice: "INV001", status: "已支付", method: "信用卡", amount: "¥250.00" },
|
|
1380
|
-
{ invoice: "INV002", status: "待处理", method: "PayPal", amount: "¥150.00" },
|
|
1381
|
-
]
|
|
1382
|
-
|
|
1383
|
-
<Table>
|
|
1384
|
-
<TableCaption>最近的发票列表</TableCaption>
|
|
1385
|
-
<TableHeader>
|
|
1386
|
-
<TableRow>
|
|
1387
|
-
<TableHead>发票号</TableHead>
|
|
1388
|
-
<TableHead>状态</TableHead>
|
|
1389
|
-
<TableHead>方式</TableHead>
|
|
1390
|
-
<TableHead className="text-right">金额</TableHead>
|
|
1391
|
-
</TableRow>
|
|
1392
|
-
</TableHeader>
|
|
1393
|
-
<TableBody>
|
|
1394
|
-
{invoices.map((inv) => (
|
|
1395
|
-
<TableRow key={inv.invoice}>
|
|
1396
|
-
<TableCell className="font-medium">{inv.invoice}</TableCell>
|
|
1397
|
-
<TableCell>{inv.status}</TableCell>
|
|
1398
|
-
<TableCell>{inv.method}</TableCell>
|
|
1399
|
-
<TableCell className="text-right">{inv.amount}</TableCell>
|
|
1400
|
-
</TableRow>
|
|
1401
|
-
))}
|
|
1402
|
-
</TableBody>
|
|
1403
|
-
<TableFooter>
|
|
1404
|
-
<TableRow>
|
|
1405
|
-
<TableCell colSpan={3}>合计</TableCell>
|
|
1406
|
-
<TableCell className="text-right">¥400.00</TableCell>
|
|
1407
|
-
</TableRow>
|
|
1408
|
-
</TableFooter>
|
|
1409
|
-
</Table>
|
|
1410
|
-
```
|
|
1411
|
-
|
|
1412
|
-
---
|
|
1413
|
-
|
|
1414
|
-
### Tabs 选项卡
|
|
1415
|
-
|
|
1416
|
-
```tsx
|
|
1417
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
1418
|
-
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
|
|
1419
|
-
import { Button } from "@/components/ui/button"
|
|
1420
|
-
import { Input } from "@/components/ui/input"
|
|
1421
|
-
import { Label } from "@/components/ui/label"
|
|
1422
|
-
|
|
1423
|
-
<Tabs defaultValue="account" className="w-full">
|
|
1424
|
-
<TabsList className="grid w-full grid-cols-2">
|
|
1425
|
-
<TabsTrigger value="account">账户</TabsTrigger>
|
|
1426
|
-
<TabsTrigger value="password">密码</TabsTrigger>
|
|
1427
|
-
</TabsList>
|
|
1428
|
-
<TabsContent value="account">
|
|
1429
|
-
<Card>
|
|
1430
|
-
<CardHeader><CardTitle>账户信息</CardTitle></CardHeader>
|
|
1431
|
-
<CardContent className="space-y-2">
|
|
1432
|
-
<Label>名称</Label>
|
|
1433
|
-
<Input defaultValue="Pedro Duarte" />
|
|
1434
|
-
</CardContent>
|
|
1435
|
-
<CardFooter><Button>保存</Button></CardFooter>
|
|
1436
|
-
</Card>
|
|
1437
|
-
</TabsContent>
|
|
1438
|
-
<TabsContent value="password">
|
|
1439
|
-
<Card>
|
|
1440
|
-
<CardHeader><CardTitle>修改密码</CardTitle></CardHeader>
|
|
1441
|
-
<CardContent className="space-y-2">
|
|
1442
|
-
<Label>当前密码</Label>
|
|
1443
|
-
<Input type="text" password />
|
|
1444
|
-
<Label>新密码</Label>
|
|
1445
|
-
<Input type="text" password />
|
|
1446
|
-
</CardContent>
|
|
1447
|
-
<CardFooter><Button>保存</Button></CardFooter>
|
|
1448
|
-
</Card>
|
|
1449
|
-
</TabsContent>
|
|
1450
|
-
</Tabs>
|
|
1451
|
-
```
|
|
1452
|
-
|
|
1453
|
-
---
|
|
1454
|
-
|
|
1455
|
-
### Textarea 文本域
|
|
1456
|
-
|
|
1457
|
-
```tsx
|
|
1458
|
-
import { Textarea } from "@/components/ui/textarea"
|
|
1459
|
-
|
|
1460
|
-
<Textarea className="h-20" placeholder="请输入内容..." />
|
|
1461
|
-
<Textarea className="h-40" placeholder="更高的文本域" />
|
|
1462
|
-
<Textarea disabled placeholder="已禁用" />
|
|
1463
|
-
```
|
|
1464
|
-
|
|
1465
|
-
---
|
|
1466
|
-
|
|
1467
|
-
### Toast 吐司通知
|
|
1468
|
-
|
|
1469
|
-
全局 API 调用,不需要 Context Provider。需要在页面中放置 `<Toaster />`。
|
|
1470
|
-
|
|
1471
|
-
```tsx
|
|
1472
|
-
import { Toaster, toast } from "@/components/ui/toast"
|
|
1473
|
-
import { Button } from "@/components/ui/button"
|
|
1474
|
-
|
|
1475
|
-
{/* 在页面中放置 Toaster */}
|
|
1476
|
-
<Toaster position="bottom-right" richColors />
|
|
1477
|
-
|
|
1478
|
-
{/* 调用 toast */}
|
|
1479
|
-
<Button onClick={() => toast("事件已创建")}>默认</Button>
|
|
1480
|
-
<Button onClick={() => toast.success("操作成功", { description: "周一下午 6 点" })}>成功</Button>
|
|
1481
|
-
<Button onClick={() => toast.error("操作失败")}>错误</Button>
|
|
1482
|
-
<Button onClick={() => toast.info("提示信息")}>信息</Button>
|
|
1483
|
-
<Button onClick={() => toast.warning("警告")}>警告</Button>
|
|
1484
|
-
|
|
1485
|
-
{/* 带操作按钮 */}
|
|
1486
|
-
<Button onClick={() => toast("已创建", {
|
|
1487
|
-
action: { label: "撤销", onClick: () => console.log("撤销") },
|
|
1488
|
-
})}>
|
|
1489
|
-
带操作
|
|
1490
|
-
</Button>
|
|
1491
|
-
|
|
1492
|
-
{/* Promise */}
|
|
1493
|
-
<Button onClick={() => toast.promise(
|
|
1494
|
-
() => new Promise((resolve) => setTimeout(() => resolve({ name: "Sonner" }), 2000)),
|
|
1495
|
-
{
|
|
1496
|
-
loading: "加载中...",
|
|
1497
|
-
success: (data: any) => `${data.name} 已添加`,
|
|
1498
|
-
error: "出错了",
|
|
1499
|
-
},
|
|
1500
|
-
)}>
|
|
1501
|
-
Promise
|
|
1502
|
-
</Button>
|
|
1503
|
-
|
|
1504
|
-
{/* 自定义组件 */}
|
|
1505
|
-
<Button onClick={() => toast.custom((t) => (
|
|
1506
|
-
<View className="w-full rounded-md border bg-background p-4 shadow-sm">
|
|
1507
|
-
<View className="text-sm font-medium">自定义 Toast</View>
|
|
1508
|
-
<View className="text-sm text-muted-foreground">ID: {t}</View>
|
|
1509
|
-
</View>
|
|
1510
|
-
))}>
|
|
1511
|
-
自定义
|
|
1512
|
-
</Button>
|
|
1513
|
-
|
|
1514
|
-
{/* 加载后更新 */}
|
|
1515
|
-
<Button onClick={() => {
|
|
1516
|
-
const id = toast.loading("加载中...")
|
|
1517
|
-
setTimeout(() => {
|
|
1518
|
-
toast.dismiss(id)
|
|
1519
|
-
toast.success("加载完成!")
|
|
1520
|
-
}, 2000)
|
|
1521
|
-
}}>
|
|
1522
|
-
加载与更新
|
|
1523
|
-
</Button>
|
|
1524
|
-
```
|
|
1525
|
-
|
|
1526
|
-
**Toaster 属性**:
|
|
1527
|
-
|
|
1528
|
-
| 属性 | 类型 | 说明 |
|
|
1529
|
-
| ------------- | ----------------------------------------------------------------------------------------------------------- | -------------- |
|
|
1530
|
-
| `position` | `"top-left"` \| `"top-center"` \| `"top-right"` \| `"bottom-left"` \| `"bottom-center"` \| `"bottom-right"` | 显示位置 |
|
|
1531
|
-
| `richColors` | `boolean` | 丰富颜色模式 |
|
|
1532
|
-
| `expand` | `boolean` | 展开所有 toast |
|
|
1533
|
-
| `closeButton` | `boolean` | 显示关闭按钮 |
|
|
1534
|
-
|
|
1535
|
-
**toast API**:
|
|
1536
|
-
- `toast(message, options?)` — 默认
|
|
1537
|
-
- `toast.success(message, options?)` — 成功
|
|
1538
|
-
- `toast.error(message, options?)` — 错误
|
|
1539
|
-
- `toast.info(message, options?)` — 信息
|
|
1540
|
-
- `toast.warning(message, options?)` — 警告
|
|
1541
|
-
- `toast.loading(message)` — 加载中,返回 id
|
|
1542
|
-
- `toast.promise(promise, { loading, success, error })` — Promise
|
|
1543
|
-
- `toast.custom((id) => JSX)` — 自定义组件
|
|
1544
|
-
- `toast.dismiss(id)` — 关闭指定 toast
|
|
1545
|
-
|
|
1546
|
-
---
|
|
1547
|
-
|
|
1548
|
-
### Toggle 切换按钮
|
|
1549
|
-
|
|
1550
|
-
```tsx
|
|
1551
|
-
import { Toggle } from "@/components/ui/toggle"
|
|
1552
|
-
import { Bold, Italic } from "lucide-react-taro"
|
|
1553
|
-
|
|
1554
|
-
<Toggle aria-label="Toggle bold"><Bold size={16} /></Toggle>
|
|
1555
|
-
<Toggle variant="outline"><Italic size={16} /></Toggle>
|
|
1556
|
-
<Toggle size="sm"><Italic size={12} /></Toggle>
|
|
1557
|
-
<Toggle size="lg"><Italic size={20} /></Toggle>
|
|
1558
|
-
<Toggle disabled><Italic size={16} /></Toggle>
|
|
1559
|
-
```
|
|
1560
|
-
|
|
1561
|
-
| 属性 | 值 |
|
|
1562
|
-
| ----------------------------- | ------------------------------- |
|
|
1563
|
-
| `variant` | `"default"` \| `"outline"` |
|
|
1564
|
-
| `size` | `"sm"` \| `"default"` \| `"lg"` |
|
|
1565
|
-
| `pressed` / `onPressedChange` | 受控模式 |
|
|
1566
|
-
|
|
1567
|
-
---
|
|
1568
|
-
|
|
1569
|
-
### ToggleGroup 切换按钮组
|
|
1570
|
-
|
|
1571
|
-
```tsx
|
|
1572
|
-
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|
1573
|
-
import { Bold, Italic, Underline } from "lucide-react-taro"
|
|
1574
|
-
|
|
1575
|
-
{/* 单选 */}
|
|
1576
|
-
<ToggleGroup type="single">
|
|
1577
|
-
<ToggleGroupItem value="bold"><Bold size={16} /></ToggleGroupItem>
|
|
1578
|
-
<ToggleGroupItem value="italic"><Italic size={16} /></ToggleGroupItem>
|
|
1579
|
-
<ToggleGroupItem value="underline"><Underline size={16} /></ToggleGroupItem>
|
|
1580
|
-
</ToggleGroup>
|
|
1581
|
-
|
|
1582
|
-
{/* 多选 + outline */}
|
|
1583
|
-
<ToggleGroup type="multiple" variant="outline">
|
|
1584
|
-
<ToggleGroupItem value="bold"><Bold size={16} /></ToggleGroupItem>
|
|
1585
|
-
<ToggleGroupItem value="italic"><Italic size={16} /></ToggleGroupItem>
|
|
1586
|
-
</ToggleGroup>
|
|
1587
|
-
```
|
|
1588
|
-
|
|
1589
|
-
| 属性 | 值 |
|
|
1590
|
-
| --------- | ------------------------------- |
|
|
1591
|
-
| `type` | `"single"` \| `"multiple"` |
|
|
1592
|
-
| `variant` | `"default"` \| `"outline"` |
|
|
1593
|
-
| `size` | `"sm"` \| `"default"` \| `"lg"` |
|
|
1594
|
-
|
|
1595
|
-
---
|
|
1596
|
-
|
|
1597
|
-
### Tooltip 工具提示
|
|
1598
|
-
|
|
1599
|
-
H5 端鼠标 hover 触发,小程序端点击触发。需要 `TooltipProvider` 包裹。
|
|
1600
|
-
|
|
1601
|
-
```tsx
|
|
1602
|
-
import {
|
|
1603
|
-
Tooltip, TooltipContent, TooltipProvider, TooltipTrigger,
|
|
1604
|
-
} from "@/components/ui/tooltip"
|
|
1605
|
-
import { Button } from "@/components/ui/button"
|
|
1606
|
-
|
|
1607
|
-
<TooltipProvider>
|
|
1608
|
-
<Tooltip>
|
|
1609
|
-
<TooltipTrigger className="inline-block">
|
|
1610
|
-
<Button variant="outline">Hover 我</Button>
|
|
1611
|
-
</TooltipTrigger>
|
|
1612
|
-
<TooltipContent side="top">
|
|
1613
|
-
<View>添加到收藏夹</View>
|
|
1614
|
-
</TooltipContent>
|
|
1615
|
-
</Tooltip>
|
|
1616
|
-
</TooltipProvider>
|
|
1617
|
-
```
|
|
1618
|
-
|
|
1619
|
-
| 属性 | 值 |
|
|
1620
|
-
| ----------------- | ---------------------------------------------- |
|
|
1621
|
-
| `side` | `"top"` \| `"right"` \| `"bottom"` \| `"left"` |
|
|
1622
|
-
| `align` | `"start"` \| `"center"` \| `"end"` |
|
|
1623
|
-
| `showArrow` | `boolean` |
|
|
1624
|
-
| `avoidCollisions` | `boolean` |
|
|
1625
|
-
|
|
1626
|
-
---
|
|
1627
|
-
|
|
1628
|
-
### CodeBlock 代码块
|
|
1629
|
-
|
|
1630
|
-
内置轻量 tokenizer 实现代码高亮,非外部语法高亮库。
|
|
1631
|
-
|
|
1632
|
-
```tsx
|
|
1633
|
-
import { CodeBlock } from "@/components/ui/code-block"
|
|
1634
|
-
|
|
1635
|
-
<CodeBlock
|
|
1636
|
-
code={`const greeting = "Hello, World!";\nconsole.log(greeting);`}
|
|
1637
|
-
language="javascript"
|
|
1638
|
-
showCopyButton
|
|
1639
|
-
/>
|
|
1640
|
-
```
|
|
1641
|
-
|
|
1642
|
-
---
|
|
1643
|
-
|
|
1644
|
-
## 组件导入路径速查
|
|
1645
|
-
|
|
1646
|
-
所有组件统一从 `@/components/ui/<组件名>` 导入:
|
|
1647
|
-
|
|
1648
|
-
```tsx
|
|
1649
|
-
import { Button } from "@/components/ui/button"
|
|
1650
|
-
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"
|
|
1651
|
-
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"
|
|
1652
|
-
import { Input } from "@/components/ui/input"
|
|
1653
|
-
import { Label } from "@/components/ui/label"
|
|
1654
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
|
1655
|
-
import { Toaster, toast } from "@/components/ui/toast"
|
|
1656
|
-
// sonner.tsx 重导出 toast.tsx 的所有内容
|
|
1657
|
-
import { Toaster, toast } from "@/components/ui/sonner"
|
|
1658
|
-
```
|
|
1659
|
-
|
|
1660
|
-
图标从 `lucide-react-taro` 导入(不是 `lucide-react`):
|
|
1661
|
-
|
|
1662
|
-
```tsx
|
|
1663
|
-
import { Check, X, ChevronDown, Search, Mail } from "lucide-react-taro"
|
|
1664
|
-
```
|
|
1665
|
-
|
|
1666
|
-
工具函数:
|
|
1667
|
-
|
|
1668
|
-
```tsx
|
|
1669
|
-
import { cn } from "@/lib/utils"
|
|
1670
|
-
```
|
|
1671
|
-
|
|
1672
|
-
## 与 Web 版 shadcn/ui 的关键差异
|
|
1673
|
-
|
|
1674
|
-
| 项目 | Web shadcn/ui | Taro shadcn/ui |
|
|
1675
|
-
| ---------- | --------------------------------------- | ----------------------------------------------- |
|
|
1676
|
-
| 底层依赖 | Radix UI | 从零实现(无外部 UI 原语库) |
|
|
1677
|
-
| DOM 元素 | `<div>`, `<button>`, `<input>` | `<View>`, `<Text>`, `<Input>` (Taro) |
|
|
1678
|
-
| 图标库 | `lucide-react` | `lucide-react-taro` |
|
|
1679
|
-
| Portal | `createPortal` | H5: `createPortal`; 小程序: `RootPortal` |
|
|
1680
|
-
| hover 效果 | CSS `:hover` | `hoverClass` 属性 |
|
|
1681
|
-
| 路由导航 | `<Link>`, `next/link` 等 | `Taro.navigateTo()` |
|
|
1682
|
-
| 动画 | `tailwindcss-animate` / `framer-motion` | `tailwindcss-animate` + CSS transition |
|
|
1683
|
-
| CSS 方案 | Tailwind CSS | Tailwind CSS + `weapp-tailwindcss` (小程序适配) |
|
|
1684
|
-
| Toast | `sonner` 外部包 | 内置实现,API 兼容 sonner |
|
|
1685
|
-
| Calendar | `react-day-picker` | 内置实现,使用 `date-fns` |
|
|
1686
|
-
| 样式覆盖 | `className` | `className`(通过 `cn()` 合并) |
|