@teamix-evo/ui 0.7.0 → 0.7.2
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/manifest.json +16 -7
- package/package.json +4 -4
- package/src/_design-system/theme-tokens/stories.tsx +2 -2
- package/src/components/accordion/index.tsx +1 -1
- package/src/components/affix/meta.md +26 -0
- package/src/components/alert/index.tsx +2 -2
- package/src/components/alert-dialog/index.tsx +3 -3
- package/src/components/alert-dialog/meta.md +52 -0
- package/src/components/alert-dialog/stories.tsx +45 -48
- package/src/components/avatar/index.tsx +1 -1
- package/src/components/badge/index.tsx +2 -2
- package/src/components/badge/meta.md +48 -0
- package/src/components/button/index.tsx +2 -2
- package/src/components/button/meta.md +15 -0
- package/src/components/button/stories.tsx +1 -1
- package/src/components/calendar/index.tsx +2 -2
- package/src/components/card/index.tsx +1 -1
- package/src/components/carousel/index.tsx +2 -2
- package/src/components/carousel/meta.md +34 -2
- package/src/components/carousel/stories.tsx +2 -2
- package/src/components/cascader-select/index.tsx +2 -1
- package/src/components/cascader-select/meta.md +46 -0
- package/src/components/checkbox/meta.md +47 -0
- package/src/components/color-picker/index.tsx +3 -3
- package/src/components/color-picker/meta.md +80 -0
- package/src/components/combobox/index.tsx +2 -2
- package/src/components/combobox/meta.md +130 -0
- package/src/components/data-table/index.tsx +3 -3
- package/src/components/data-table/meta.md +419 -0
- package/src/components/data-table/stories.tsx +4 -4
- package/src/components/date-picker/meta.md +91 -0
- package/src/components/descriptions/index.tsx +1 -1
- package/src/components/descriptions/meta.md +245 -0
- package/src/components/dialog/index.tsx +4 -4
- package/src/components/dialog/meta.md +47 -1
- package/src/components/dialog/stories.tsx +38 -41
- package/src/components/dropdown-menu/index.tsx +5 -5
- package/src/components/empty/index.tsx +2 -2
- package/src/components/field/index.tsx +4 -4
- package/src/components/filter-bar/index.tsx +6 -6
- package/src/components/filter-bar/meta.md +323 -0
- package/src/components/float-button/index.tsx +2 -2
- package/src/components/form/index.tsx +1 -1
- package/src/components/form/meta.md +119 -0
- package/src/components/hover-card/index.tsx +1 -1
- package/src/components/hover-card/meta.md +21 -0
- package/src/components/input/meta.md +16 -0
- package/src/components/input-group/index.tsx +1 -1
- package/src/components/input-group/meta.md +118 -0
- package/src/components/input-group/stories.tsx +6 -6
- package/src/components/input-ip/index.tsx +2 -2
- package/src/components/input-ip/meta.md +30 -0
- package/src/components/input-ip/stories.tsx +2 -2
- package/src/components/input-number/index.tsx +3 -2
- package/src/components/input-number/meta.md +67 -0
- package/src/components/input-number/stories.tsx +2 -2
- package/src/components/item/index.tsx +4 -4
- package/src/components/label/meta.md +8 -0
- package/src/components/mentions/meta.md +15 -0
- package/src/components/menubar/index.tsx +4 -4
- package/src/components/navigation-menu/index.tsx +4 -4
- package/src/components/page-header/index.tsx +2 -2
- package/src/components/page-header/meta.md +145 -0
- package/src/components/page-shell/index.tsx +3 -3
- package/src/components/pagination/index.tsx +1 -1
- package/src/components/pagination/meta.md +203 -0
- package/src/components/popconfirm/meta.md +45 -0
- package/src/components/popover/index.tsx +2 -2
- package/src/components/popover/meta.md +47 -0
- package/src/components/progress/index.tsx +1 -1
- package/src/components/progress/meta.md +36 -0
- package/src/components/progress/stories.tsx +1 -1
- package/src/components/radio-group/meta.md +69 -0
- package/src/components/rate/index.tsx +1 -1
- package/src/components/rate/meta.md +50 -0
- package/src/components/resizable/index.tsx +1 -1
- package/src/components/select/index.tsx +2 -2
- package/src/components/select/meta.md +20 -0
- package/src/components/separator/index.tsx +1 -1
- package/src/components/sheet/index.tsx +13 -14
- package/src/components/sheet/meta.md +124 -0
- package/src/components/sheet/stories.tsx +110 -119
- package/src/components/sidebar/index.tsx +5 -5
- package/src/components/sidebar/meta.md +383 -0
- package/src/components/skeleton/meta.md +13 -0
- package/src/components/slider/index.tsx +2 -2
- package/src/components/sonner/meta.md +86 -0
- package/src/components/spinner/meta.md +46 -0
- package/src/components/spinner/stories.tsx +2 -2
- package/src/components/steps/meta.md +20 -0
- package/src/components/steps/stories.tsx +1 -1
- package/src/components/switch/index.tsx +2 -2
- package/src/components/switch/meta.md +33 -0
- package/src/components/table/index.tsx +4 -4
- package/src/components/table/meta.md +11 -0
- package/src/components/tabs/index.tsx +7 -7
- package/src/components/tabs/meta.md +52 -0
- package/src/components/tag/index.tsx +8 -8
- package/src/components/tag/meta.md +194 -0
- package/src/components/textarea/index.tsx +1 -1
- package/src/components/textarea/meta.md +27 -0
- package/src/components/textarea/stories.tsx +1 -1
- package/src/components/time-picker/index.tsx +3 -3
- package/src/components/time-picker/meta.md +76 -0
- package/src/components/timeline/index.tsx +1 -0
- package/src/components/toggle/index.tsx +1 -1
- package/src/components/toggle-group/index.tsx +1 -1
- package/src/components/tooltip/index.tsx +1 -1
- package/src/components/tooltip/meta.md +23 -0
- package/src/components/transfer/index.tsx +2 -2
- package/src/components/transfer/meta.md +97 -0
- package/src/components/tree/index.tsx +245 -15
- package/src/components/tree/meta.md +151 -0
- package/src/components/tree-select/index.tsx +16 -2
- package/src/components/tree-select/meta.md +150 -0
- package/src/components/typography/index.tsx +3 -3
- package/src/components/upload/index.tsx +3 -3
- package/src/components/upload/meta.md +82 -0
- package/src/components/tree/utils.ts +0 -269
- package/src/examples/built-in-assets/stories.tsx +0 -572
- package/src/examples/evaluators/stories.tsx +0 -502
|
@@ -69,6 +69,176 @@ Table primitives + Pagination 实现 排序 / 过滤 / 分页 / 行选择 / 行
|
|
|
69
69
|
|
|
70
70
|
## 示例
|
|
71
71
|
|
|
72
|
+
### Full
|
|
73
|
+
|
|
74
|
+
完整页面组合:展示 DataTable 在真实业务页面中的使用方式。 体现 shadcn 组合式理念:页面级布局由多个独立组件自由组合,DataTable 只负责表格渲染, Toolbar / FilterBar / Typography 等是兄弟组件,通过外部 state 与 DataTable 通信。
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<label
|
|
78
|
+
key={id}
|
|
79
|
+
className="flex cursor-pointer items-center gap-2 text-xs"
|
|
80
|
+
>
|
|
81
|
+
<Checkbox
|
|
82
|
+
checked={checked}
|
|
83
|
+
onCheckedChange={(c) =>
|
|
84
|
+
setVisibility((prev) => ({
|
|
85
|
+
...prev,
|
|
86
|
+
[id]: c === true,
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
/>
|
|
90
|
+
<span>
|
|
91
|
+
{typeof col.title === 'string' ? col.title : id}
|
|
92
|
+
</span>
|
|
93
|
+
</label>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### ColumnVisibility
|
|
97
|
+
|
|
98
|
+
列显隐:外部 Toolbar 通过 `columnVisibility` + `onColumnVisibilityChange` 控制列显示/隐藏。
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<label
|
|
102
|
+
key={id}
|
|
103
|
+
className="flex cursor-pointer items-center gap-2 text-xs"
|
|
104
|
+
>
|
|
105
|
+
<Checkbox
|
|
106
|
+
checked={checked}
|
|
107
|
+
onCheckedChange={(c) =>
|
|
108
|
+
setVisibility((prev) => ({
|
|
109
|
+
...prev,
|
|
110
|
+
[id]: c === true,
|
|
111
|
+
}))
|
|
112
|
+
}
|
|
113
|
+
/>
|
|
114
|
+
<span>
|
|
115
|
+
{typeof col.title === 'string' ? col.title : id}
|
|
116
|
+
</span>
|
|
117
|
+
</label>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### WithSorting
|
|
121
|
+
|
|
122
|
+
列排序:通过 `sortable: true` 启用,单击表头切换 asc / desc / 无。
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
const columns: DataTableColumn<User>[] = [
|
|
126
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 100 },
|
|
127
|
+
{
|
|
128
|
+
key: 'age',
|
|
129
|
+
dataIndex: 'age',
|
|
130
|
+
title: '年龄',
|
|
131
|
+
width: 100,
|
|
132
|
+
align: 'right',
|
|
133
|
+
sortable: true,
|
|
134
|
+
},
|
|
135
|
+
{ key: 'role', dataIndex: 'role', title: '角色', width: 120 },
|
|
136
|
+
{ key: 'city', dataIndex: 'city', title: '城市' },
|
|
137
|
+
];
|
|
138
|
+
return <DataTable columns={columns} data={users} />;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### WithFiltering
|
|
142
|
+
|
|
143
|
+
列过滤:`filters` 提供候选项,单击表头过滤图标弹出 Popover 选择。
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
const columns: DataTableColumn<User>[] = [
|
|
147
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 100 },
|
|
148
|
+
{
|
|
149
|
+
key: 'age',
|
|
150
|
+
dataIndex: 'age',
|
|
151
|
+
title: '年龄',
|
|
152
|
+
width: 80,
|
|
153
|
+
align: 'right',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
key: 'role',
|
|
157
|
+
dataIndex: 'role',
|
|
158
|
+
title: '角色',
|
|
159
|
+
width: 120,
|
|
160
|
+
filters: [
|
|
161
|
+
{ label: '管理员', value: '管理员' },
|
|
162
|
+
{ label: '开发', value: '开发' },
|
|
163
|
+
{ label: '运维', value: '运维' },
|
|
164
|
+
{ label: '产品', value: '产品' },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
key: 'status',
|
|
169
|
+
dataIndex: 'status',
|
|
170
|
+
title: '状态',
|
|
171
|
+
width: 100,
|
|
172
|
+
filterMode: 'single',
|
|
173
|
+
filters: [
|
|
174
|
+
{ label: '活跃', value: 'active' },
|
|
175
|
+
{ label: '暂停', value: 'paused' },
|
|
176
|
+
{ label: '已封', value: 'banned' },
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{ key: 'city', dataIndex: 'city', title: '城市' },
|
|
180
|
+
];
|
|
181
|
+
return <DataTable columns={columns} data={users} />;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### WithPagination
|
|
185
|
+
|
|
186
|
+
分页:默认开启分页,可通过 `pagination` 配置 pageSize / showSizeChanger / showTotal。
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
<DataTable
|
|
190
|
+
columns={baseColumns}
|
|
191
|
+
data={data}
|
|
192
|
+
pagination={{
|
|
193
|
+
defaultPageSize: 10,
|
|
194
|
+
pageSizeOptions: [5, 10, 20],
|
|
195
|
+
showSizeChanger: true,
|
|
196
|
+
showTotal: true,
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### RowSelectionMultiple
|
|
202
|
+
|
|
203
|
+
多选行:`rowSelection` 多选模式(默认),表头全选 / 半选。
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
<div className="flex flex-col gap-2">
|
|
207
|
+
<p className="text-xs text-muted-foreground">
|
|
208
|
+
已选:{keys.length} 项 — {keys.join(', ') || '无'}
|
|
209
|
+
</p>
|
|
210
|
+
<DataTable
|
|
211
|
+
columns={baseColumns}
|
|
212
|
+
data={users}
|
|
213
|
+
rowSelection={{
|
|
214
|
+
selectedRowKeys: keys,
|
|
215
|
+
onChange: (k) => setKeys(k),
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### RowSelectionSingle
|
|
222
|
+
|
|
223
|
+
单选行:`rowSelection.mode = 'single'`。
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
<div className="flex flex-col gap-2">
|
|
227
|
+
<p className="text-xs text-muted-foreground">
|
|
228
|
+
当前选中:{keys[0] ?? '无'}
|
|
229
|
+
</p>
|
|
230
|
+
<DataTable
|
|
231
|
+
columns={baseColumns}
|
|
232
|
+
data={users}
|
|
233
|
+
rowSelection={{
|
|
234
|
+
mode: 'single',
|
|
235
|
+
selectedRowKeys: keys,
|
|
236
|
+
onChange: (k) => setKeys(k),
|
|
237
|
+
}}
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
```
|
|
241
|
+
|
|
72
242
|
### ExpandedRow
|
|
73
243
|
|
|
74
244
|
行展开:`expandable.expandedRowRender` 渲染额外区域。
|
|
@@ -105,3 +275,252 @@ Loading 与 Empty 状态。
|
|
|
105
275
|
</div>
|
|
106
276
|
</div>
|
|
107
277
|
```
|
|
278
|
+
|
|
279
|
+
### CustomCell
|
|
280
|
+
|
|
281
|
+
自定义单元格渲染 + 操作列。
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
const columns: DataTableColumn<User>[] = [
|
|
285
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 100 },
|
|
286
|
+
{
|
|
287
|
+
key: 'age',
|
|
288
|
+
dataIndex: 'age',
|
|
289
|
+
title: '年龄',
|
|
290
|
+
width: 80,
|
|
291
|
+
align: 'right',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
key: 'status',
|
|
295
|
+
dataIndex: 'status',
|
|
296
|
+
title: '状态',
|
|
297
|
+
width: 100,
|
|
298
|
+
render: (val) => {
|
|
299
|
+
const v = val as User['status'];
|
|
300
|
+
const map = {
|
|
301
|
+
active: { label: '活跃', status: 'success' as const },
|
|
302
|
+
paused: { label: '暂停', status: 'warning' as const },
|
|
303
|
+
banned: { label: '已封', status: 'error' as const },
|
|
304
|
+
};
|
|
305
|
+
return (
|
|
306
|
+
<Tag variant="status" status={map[v].status} showIcon>
|
|
307
|
+
{map[v].label}
|
|
308
|
+
</Tag>
|
|
309
|
+
);
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{ key: 'city', dataIndex: 'city', title: '城市' },
|
|
313
|
+
{
|
|
314
|
+
key: 'actions',
|
|
315
|
+
title: '操作',
|
|
316
|
+
width: 120,
|
|
317
|
+
align: 'right',
|
|
318
|
+
cell: () => (
|
|
319
|
+
<ButtonGroup className="h-4 gap-3">
|
|
320
|
+
<Button variant="link">编辑</Button>
|
|
321
|
+
<ButtonGroupSeparator />
|
|
322
|
+
<Button
|
|
323
|
+
variant="link"
|
|
324
|
+
className="text-destructive hover:text-destructive"
|
|
325
|
+
>
|
|
326
|
+
删除
|
|
327
|
+
</Button>
|
|
328
|
+
</ButtonGroup>
|
|
329
|
+
),
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
return <DataTable columns={columns} data={users.slice(0, 5)} />;
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### LockColumns
|
|
336
|
+
|
|
337
|
+
锁列:通过 `fixed: 'left' | 'right'` 让列在水平滚动时保持位置。
|
|
338
|
+
|
|
339
|
+
```tsx
|
|
340
|
+
<div style={{ width: 720 }}>
|
|
341
|
+
<DataTable columns={columns} data={wideUsers} />
|
|
342
|
+
</div>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### ResizableColumns
|
|
346
|
+
|
|
347
|
+
列宽拖拽:`enableColumnResizing` 全局开启,列上 `resizable: true` 启用,鼠标拖拽列右侧分隔线。
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
<div className="flex flex-col gap-2">
|
|
351
|
+
<p className="text-xs text-muted-foreground">
|
|
352
|
+
当前列宽:{JSON.stringify(sizing)}
|
|
353
|
+
</p>
|
|
354
|
+
<DataTable
|
|
355
|
+
columns={columns}
|
|
356
|
+
data={users}
|
|
357
|
+
enableColumnResizing
|
|
358
|
+
columnSizing={sizing}
|
|
359
|
+
onColumnSizingChange={setSizing}
|
|
360
|
+
/>
|
|
361
|
+
</div>
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### TreeData
|
|
365
|
+
|
|
366
|
+
树形数据:`isTree` + `children` 字段递归渲染,子行通过缩进 + 展开按钮表达层级。
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
<DataTable
|
|
370
|
+
columns={columns}
|
|
371
|
+
data={treeUsers}
|
|
372
|
+
isTree
|
|
373
|
+
indentSize={20}
|
|
374
|
+
pagination={false}
|
|
375
|
+
/>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### FixedHeader
|
|
379
|
+
|
|
380
|
+
固定表头:`stickyHeader` + `maxBodyHeight` 让表头滚动吸顶。
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
<DataTable
|
|
384
|
+
columns={baseColumns}
|
|
385
|
+
data={data}
|
|
386
|
+
stickyHeader
|
|
387
|
+
maxBodyHeight={320}
|
|
388
|
+
pagination={false}
|
|
389
|
+
/>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Crossline
|
|
393
|
+
|
|
394
|
+
十字辅助轴:`crossline` 同时高亮 hover 行与列,便于宽表格定位。
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
const columns: DataTableColumn<User>[] = [
|
|
398
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 120 },
|
|
399
|
+
{
|
|
400
|
+
key: 'age',
|
|
401
|
+
dataIndex: 'age',
|
|
402
|
+
title: '年龄',
|
|
403
|
+
width: 80,
|
|
404
|
+
align: 'right',
|
|
405
|
+
},
|
|
406
|
+
{ key: 'role', dataIndex: 'role', title: '角色', width: 120 },
|
|
407
|
+
{ key: 'status', dataIndex: 'status', title: '状态', width: 120 },
|
|
408
|
+
{ key: 'city', dataIndex: 'city', title: '城市', width: 120 },
|
|
409
|
+
];
|
|
410
|
+
return <DataTable columns={columns} data={users} crossline />;
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Virtual
|
|
414
|
+
|
|
415
|
+
虚拟滚动:`virtual` 开启后只渲染可视区域的行,适合千条以上数据。需同时设置 `maxBodyHeight`。
|
|
416
|
+
|
|
417
|
+
```tsx
|
|
418
|
+
<div className="flex flex-col gap-2">
|
|
419
|
+
<p className="text-xs text-muted-foreground">
|
|
420
|
+
总计 {data.length} 条,开启虚拟滚动后仅渲染可见区域。
|
|
421
|
+
</p>
|
|
422
|
+
<DataTable
|
|
423
|
+
columns={baseColumns}
|
|
424
|
+
data={data}
|
|
425
|
+
virtual
|
|
426
|
+
stickyHeader
|
|
427
|
+
maxBodyHeight={400}
|
|
428
|
+
estimateRowHeight={40}
|
|
429
|
+
pagination={false}
|
|
430
|
+
/>
|
|
431
|
+
</div>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### MultiHeader
|
|
435
|
+
|
|
436
|
+
多级表头:列定义中传入 `children` 数组即可表示分组,头部渲染为两层 `<thead><tr>`。
|
|
437
|
+
|
|
438
|
+
```tsx
|
|
439
|
+
const columns: DataTableColumn<User>[] = [
|
|
440
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 120 },
|
|
441
|
+
{
|
|
442
|
+
key: 'profile',
|
|
443
|
+
title: '个人信息',
|
|
444
|
+
children: [
|
|
445
|
+
{
|
|
446
|
+
key: 'age',
|
|
447
|
+
dataIndex: 'age',
|
|
448
|
+
title: '年龄',
|
|
449
|
+
width: 80,
|
|
450
|
+
align: 'right',
|
|
451
|
+
},
|
|
452
|
+
{ key: 'city', dataIndex: 'city', title: '城市', width: 120 },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
key: 'work',
|
|
457
|
+
title: '工作信息',
|
|
458
|
+
children: [
|
|
459
|
+
{ key: 'role', dataIndex: 'role', title: '角色', width: 120 },
|
|
460
|
+
{ key: 'status', dataIndex: 'status', title: '状态', width: 120 },
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
];
|
|
464
|
+
return <DataTable columns={columns} data={users} />;
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### MergedCells
|
|
468
|
+
|
|
469
|
+
合并单元格:`cellProps` 返回 `{ rowSpan, colSpan }`,返回 0 则跳过该 td(与 TanStack 原生渲染拼接)。
|
|
470
|
+
|
|
471
|
+
```tsx
|
|
472
|
+
const columns: DataTableColumn<User>[] = [
|
|
473
|
+
{ key: 'name', dataIndex: 'name', title: '姓名', width: 100 },
|
|
474
|
+
{ key: 'role', dataIndex: 'role', title: '角色', width: 120 },
|
|
475
|
+
{ key: 'city', dataIndex: 'city', title: '城市', width: 120 },
|
|
476
|
+
{ key: 'status', dataIndex: 'status', title: '状态', width: 120 },
|
|
477
|
+
];
|
|
478
|
+
// 示例:头三行 “角色” 同为 “管理员” 时合并 rowSpan=3;后面两行返回 rowSpan=0 跳过。
|
|
479
|
+
const cellProps = (rowIndex: number, colIndex: number) => {
|
|
480
|
+
// colIndex 1 对应 role 列
|
|
481
|
+
if (colIndex !== 1) return undefined;
|
|
482
|
+
if (rowIndex === 0) return { rowSpan: 3 };
|
|
483
|
+
if (rowIndex === 1 || rowIndex === 2) return { rowSpan: 0 };
|
|
484
|
+
return undefined;
|
|
485
|
+
};
|
|
486
|
+
const data = [
|
|
487
|
+
{ ...users[0]!, role: '管理员' as const },
|
|
488
|
+
{ ...users[1]!, role: '管理员' as const },
|
|
489
|
+
{ ...users[2]!, role: '管理员' as const },
|
|
490
|
+
...users.slice(3),
|
|
491
|
+
];
|
|
492
|
+
return <DataTable columns={columns} data={data} cellProps={cellProps} />;
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### EditableCellStory
|
|
496
|
+
|
|
497
|
+
可编辑单元格:`EditableCell` helper 双击切入输入框,Enter / blur 提交,Esc 取消。
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
<div className="flex flex-col gap-2">
|
|
501
|
+
<p className="text-xs text-muted-foreground">
|
|
502
|
+
双击 “姓名” 或 “年龄” 单元格可进入编辑态。
|
|
503
|
+
</p>
|
|
504
|
+
<DataTable columns={columns} data={data} pagination={false} />
|
|
505
|
+
</div>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### DraggableRow
|
|
509
|
+
|
|
510
|
+
行拖拽排序:`draggable` 开启后,首列出现拖拽手柄,拖拽排序后触发 `onDragEnd`。
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
<div className="flex flex-col gap-2">
|
|
514
|
+
<p className="text-xs text-muted-foreground">
|
|
515
|
+
拖拽首列手柄调整行顺序。
|
|
516
|
+
</p>
|
|
517
|
+
<DataTable
|
|
518
|
+
columns={baseColumns}
|
|
519
|
+
data={data}
|
|
520
|
+
rowKey="id"
|
|
521
|
+
draggable
|
|
522
|
+
onDragEnd={({ data: next }) => setData(next)}
|
|
523
|
+
pagination={false}
|
|
524
|
+
/>
|
|
525
|
+
</div>
|
|
526
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import {
|
|
3
|
+
import { ArrowUpDown, RotateCw, Settings } from 'lucide-react';
|
|
4
4
|
import { useForm } from 'react-hook-form';
|
|
5
5
|
|
|
6
6
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/avatar';
|
|
@@ -380,19 +380,19 @@ export const Full: Story = {
|
|
|
380
380
|
addonAfter={
|
|
381
381
|
<div className="flex items-center gap-1">
|
|
382
382
|
<Button variant="outline" size="icon">
|
|
383
|
-
<
|
|
383
|
+
<RotateCw className="text-muted-foreground" />
|
|
384
384
|
</Button>
|
|
385
385
|
<Button
|
|
386
386
|
variant="outline"
|
|
387
387
|
size="icon"
|
|
388
388
|
onClick={() => setSize(size === 'sm' ? 'md' : 'sm')}
|
|
389
389
|
>
|
|
390
|
-
<
|
|
390
|
+
<ArrowUpDown className="text-muted-foreground" />
|
|
391
391
|
</Button>
|
|
392
392
|
<Popover>
|
|
393
393
|
<PopoverTrigger asChild>
|
|
394
394
|
<Button variant="outline" size="icon">
|
|
395
|
-
<
|
|
395
|
+
<Settings className="text-muted-foreground" />
|
|
396
396
|
</Button>
|
|
397
397
|
</PopoverTrigger>
|
|
398
398
|
<PopoverContent align="end" className="w-40 p-2">
|
|
@@ -80,6 +80,51 @@
|
|
|
80
80
|
</div>
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Controlled
|
|
84
|
+
|
|
85
|
+
受控
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<div className="flex flex-col gap-2">
|
|
89
|
+
<DatePicker value={value} onChange={setValue} />
|
|
90
|
+
<p className="text-xs text-muted-foreground">
|
|
91
|
+
{value?.toLocaleDateString() ?? '(空)'}
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### WithShowTime
|
|
97
|
+
|
|
98
|
+
带时间选择
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<DatePicker showTime placeholder="选择日期时间" />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### MonthPicker
|
|
105
|
+
|
|
106
|
+
月份选择
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<DatePicker picker="month" />
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### YearPicker
|
|
113
|
+
|
|
114
|
+
年份选择
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<DatePicker picker="year" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### WithPresets
|
|
121
|
+
|
|
122
|
+
快捷选项
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<DatePicker presets />
|
|
126
|
+
```
|
|
127
|
+
|
|
83
128
|
### DisabledDates
|
|
84
129
|
|
|
85
130
|
禁用部分日期
|
|
@@ -91,6 +136,14 @@
|
|
|
91
136
|
/>
|
|
92
137
|
```
|
|
93
138
|
|
|
139
|
+
### ErrorStatus
|
|
140
|
+
|
|
141
|
+
错误状态
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
<DatePicker status="error" placeholder="请选择日期" />
|
|
145
|
+
```
|
|
146
|
+
|
|
94
147
|
### Loading
|
|
95
148
|
|
|
96
149
|
加载态:右侧图标替换为 spinner,loading > clear > CalendarIcon 互斥。
|
|
@@ -101,3 +154,41 @@
|
|
|
101
154
|
<DateRangePicker loading />
|
|
102
155
|
</div>
|
|
103
156
|
```
|
|
157
|
+
|
|
158
|
+
### Range
|
|
159
|
+
|
|
160
|
+
日期范围选择器
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<DateRangePicker />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### RangeControlled
|
|
167
|
+
|
|
168
|
+
日期范围 - 受控
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
<div className="flex flex-col gap-2">
|
|
172
|
+
<DateRangePicker value={value} onChange={setValue} />
|
|
173
|
+
<p className="text-xs text-muted-foreground">
|
|
174
|
+
{value?.from?.toLocaleDateString()} ~{' '}
|
|
175
|
+
{value?.to?.toLocaleDateString()}
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### RangeWithTime
|
|
181
|
+
|
|
182
|
+
日期范围 + 时间
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<DateRangePicker showTime />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### RangeWithPresets
|
|
189
|
+
|
|
190
|
+
日期范围 + 快捷选项
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<DateRangePicker presets />
|
|
194
|
+
```
|
|
@@ -474,7 +474,7 @@ function DescriptionsLabel({
|
|
|
474
474
|
// —— label 本身取文本行高(约 12px font × 1.5 ≈ 18px),与 content 中 24px 的 sm Button / Tag 顶对齐会产生
|
|
475
475
|
// 基线偏误;强制 min-h-6 + inline-flex items-center 后,label 框同样 24px,文字垂直居中,与
|
|
476
476
|
// 控件内文字基线自然对齐。如 content 是 h-8 / h-10 等更高控件,在 <DescriptionsItem align="center"> 带动。
|
|
477
|
-
!ctx.bordered && horizontal && 'min-
|
|
477
|
+
!ctx.bordered && horizontal && 'min-h-6 min-w-24',
|
|
478
478
|
className,
|
|
479
479
|
)}
|
|
480
480
|
{...props}
|