@teamix-evo/ui 0.5.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamix-evo/ui",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Source-injected UI components for Teamix Evo (shadcn-based, antd capabilities)",
5
5
  "type": "module",
6
6
  "files": [
@@ -51,9 +51,9 @@
51
51
  "vite": "^5.4.0",
52
52
  "vite-tsconfig-paths": "^6.1.1",
53
53
  "zod": "^3",
54
+ "@teamix-evo/registry": "0.9.0",
54
55
  "@teamix-evo/eslint-config": "0.2.3",
55
- "@teamix-evo/tokens": "^0.7.0",
56
- "@teamix-evo/registry": "0.7.0"
56
+ "@teamix-evo/tokens": "^0.7.1"
57
57
  },
58
58
  "publishConfig": {
59
59
  "access": "public",
@@ -76,6 +76,8 @@
76
76
  "build": "echo 'pure resource package, no build needed'",
77
77
  "gen:theme-overrides": "tsx .storybook/gen-theme-overrides.ts",
78
78
  "storybook": "pnpm gen:theme-overrides && storybook dev -p 6006",
79
- "build-storybook": "pnpm gen:theme-overrides && storybook build"
79
+ "build-storybook": "pnpm gen:theme-overrides && storybook build",
80
+ "compare": "tsx compare/start.ts",
81
+ "compare:matrix": "tsx compare/scripts/extract-styles-matrix.ts && tsx compare/scripts/generate-style-mapping.ts"
80
82
  }
81
83
  }
@@ -19,7 +19,7 @@ import { Spinner } from '@/components/spinner';
19
19
  // ─── Button cva ─────────────────────────────────────────────────────────────
20
20
  //
21
21
  // 视觉由 token 驱动,组件只用语义 utility:
22
- // - 圆角 `rounded-md`:opentrek=8px / uni-manager=2px
22
+ // - 圆角 `rounded-md`:走 --radius-md(opentrek=8px / uni-manager=2px),各变体 theme.css 驱动
23
23
  // - focus ring `ring-ring`:opentrek=品牌色 halo / uni-manager=transparent
24
24
  // - shadow:组件不写,uni-manager 通过 theme.css scoped CSS [data-slot='button'] 注入
25
25
 
@@ -39,7 +39,7 @@ const buttonVariants = cva(
39
39
  dashed:
40
40
  'border border-dashed border-input bg-card hover:bg-muted hover:text-foreground aria-expanded:bg-muted',
41
41
  ghost:
42
- 'hover:bg-accent hover:text-accent-foreground aria-expanded:bg-accent aria-expanded:text-accent-foreground',
42
+ 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground',
43
43
  link: 'text-primary hover:text-primary-hover',
44
44
  },
45
45
  // 语义色 — 与 variant 双 prop 组合
@@ -84,6 +84,9 @@ export const IconOnly: Story = {
84
84
  <Button size="icon-lg" aria-label="bell">
85
85
  <BellIcon />
86
86
  </Button>
87
+ <Button size="icon" variant="link" aria-label="mail">
88
+ <MailIcon />
89
+ </Button>
87
90
  </div>
88
91
  ),
89
92
  };
@@ -442,17 +442,22 @@ function ColumnFilterPopover({
442
442
  return (
443
443
  <Popover open={open} onOpenChange={setOpen}>
444
444
  <PopoverTrigger asChild>
445
- <button
446
- type="button"
445
+ <span
446
+ role="button"
447
447
  aria-label="过滤"
448
+ tabIndex={-1}
448
449
  className={cn(
449
- 'inline-flex size-5 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',
450
+ 'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground',
450
451
  active && 'text-primary',
451
452
  )}
453
+ onPointerDown={(e) => {
454
+ e.preventDefault();
455
+ e.stopPropagation();
456
+ }}
452
457
  onClick={(e) => e.stopPropagation()}
453
458
  >
454
459
  <FilterIcon className="size-3.5" />
455
- </button>
460
+ </span>
456
461
  </PopoverTrigger>
457
462
  <PopoverContent align="start" className="w-44 gap-2 p-2">
458
463
  {mode === 'multiple' ? (
@@ -498,7 +503,7 @@ function ColumnFilterPopover({
498
503
  </RadioGroup>
499
504
  )}
500
505
  <div className="mt-1 flex justify-end gap-2 border-t border-border pt-2">
501
- <Button variant="ghost" size="sm" onClick={handleReset}>
506
+ <Button variant="outline" size="sm" onClick={handleReset}>
502
507
  重置
503
508
  </Button>
504
509
  <Button size="sm" onClick={handleConfirm}>
@@ -17,7 +17,7 @@ import { cn } from '@/lib/utils';
17
17
  // ─── Input cva ──────────────────────────────────────────────────────────────
18
18
 
19
19
  const inputVariants = cva(
20
- 'w-full min-w-0 cursor-text rounded-md border border-input bg-transparent font-normal transition-colors outline-none file:inline-flex file:cursor-pointer file:border-0 file:bg-transparent file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20',
20
+ 'w-full min-w-0 cursor-text rounded-md border border-input bg-transparent font-normal transition-colors outline-none file:inline-flex file:cursor-pointer file:border-0 file:bg-transparent file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/20 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20',
21
21
  {
22
22
  variants: {
23
23
  size: {
@@ -163,10 +163,7 @@ function InputGroupTextarea({
163
163
  // ─── InputGroupClear ──────────────────────────────────────────────────────────────
164
164
 
165
165
  export interface InputGroupClearProps
166
- extends Omit<
167
- React.ComponentProps<typeof InputGroupButton>,
168
- 'children' | 'aria-label'
169
- > {
166
+ extends Omit<React.ComponentProps<'span'>, 'children'> {
170
167
  /** 是否显示。受控用法下一般绑定 `value.length > 0`。 @default true */
171
168
  visible?: boolean;
172
169
  /** 无障碍 label。 @default '清除' */
@@ -181,32 +178,36 @@ function InputGroupClear({
181
178
  visible = true,
182
179
  className,
183
180
  'aria-label': ariaLabel = '清除',
181
+ onClick,
184
182
  ...props
185
183
  }: InputGroupClearProps) {
186
184
  if (!visible) return null;
187
185
  return (
188
- <InputGroupButton
189
- data-slot="input-group-clear"
190
- size="icon-xs"
186
+ <span
187
+ role="button"
191
188
  aria-label={ariaLabel}
189
+ tabIndex={-1}
190
+ data-slot="input-group-clear"
191
+ onPointerDown={(e) => {
192
+ e.preventDefault();
193
+ e.stopPropagation();
194
+ }}
195
+ onClick={onClick}
192
196
  className={cn(
193
- 'text-muted-foreground hover:bg-transparent hover:text-foreground',
197
+ 'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground',
194
198
  className,
195
199
  )}
196
200
  {...props}
197
201
  >
198
- <XIcon />
199
- </InputGroupButton>
202
+ <XIcon className="size-3.5" />
203
+ </span>
200
204
  );
201
205
  }
202
206
 
203
207
  // ─── InputGroupPasswordToggle ───────────────────────────────────────────────────────
204
208
 
205
209
  export interface InputGroupPasswordToggleProps
206
- extends Omit<
207
- React.ComponentProps<typeof InputGroupButton>,
208
- 'children' | 'onClick'
209
- > {
210
+ extends Omit<React.ComponentProps<'span'>, 'children' | 'onClick'> {
210
211
  /** 当前密码是否可见。 */
211
212
  visible: boolean;
212
213
  /** 切换可见性,由父级同步修改 `<InputGroupInput type>` 。 */
@@ -224,20 +225,29 @@ function InputGroupPasswordToggle({
224
225
  ...props
225
226
  }: InputGroupPasswordToggleProps) {
226
227
  return (
227
- <InputGroupButton
228
- data-slot="input-group-password-toggle"
229
- size="icon-xs"
228
+ <span
229
+ role="button"
230
230
  aria-label={visible ? '隐藏密码' : '显示密码'}
231
231
  aria-pressed={visible}
232
+ tabIndex={-1}
233
+ data-slot="input-group-password-toggle"
234
+ onPointerDown={(e) => {
235
+ e.preventDefault();
236
+ e.stopPropagation();
237
+ }}
232
238
  onClick={() => onVisibleChange(!visible)}
233
239
  className={cn(
234
- 'text-muted-foreground hover:bg-transparent hover:text-foreground',
240
+ 'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:text-foreground',
235
241
  className,
236
242
  )}
237
243
  {...props}
238
244
  >
239
- {visible ? <EyeOffIcon /> : <EyeIcon />}
240
- </InputGroupButton>
245
+ {visible ? (
246
+ <EyeOffIcon className="size-3.5" />
247
+ ) : (
248
+ <EyeIcon className="size-3.5" />
249
+ )}
250
+ </span>
241
251
  );
242
252
  }
243
253
 
@@ -16,20 +16,20 @@
16
16
  - 图文卡片(horizontal 布局)
17
17
  - 通知/消息列表
18
18
  - 带操作的资源列表
19
- - 组合结构:ItemGroup [bordered] [divider] > ItemGroupHeader + Item* > ItemMedia + ItemContent (> ItemTitle + ItemDescription) + ItemExtra + ItemActions [separated] > ItemGroupFooter
19
+ - 组合结构:ItemGroup [bordered] [divider] > ItemGroupHeader + Item\* > ItemMedia + ItemContent (> ItemTitle + ItemDescription) + ItemExtra + ItemActions [separated] > ItemGroupFooter
20
20
 
21
21
  ## Props
22
22
 
23
- | 名称 | 类型 | 默认值 | 必填 | 说明 |
24
- | --- | --- | --- | --- | --- |
25
- | `bordered` | `"true" \| "false"` | – | – | – |
26
- | `divider` | `"true" \| "false"` | – | – | – |
27
- | `variant` | `"default" \| "outline" \| "muted"` | `"default"` | – | – |
28
- | `size` | `"default" \| "sm" \| "xs"` | `"default"` | – | – |
29
- | `layout` | `"vertical" \| "horizontal"` | `"vertical"` | – | – |
30
- | `asChild` | `boolean` | – | – | 使用 Slot 模式渲染子元素 |
31
- | `variant` | `"default" \| "icon" \| "image" \| "cover"` | `"default"` | – | – |
32
- | `separated` | `boolean` | – | – | 子元素之间自动插入竖向分隔线 |
23
+ | 名称 | 类型 | 默认值 | 必填 | 说明 |
24
+ | ----------- | ------------------------------------------- | ------------ | ---- | ---------------------------- |
25
+ | `bordered` | `"true" \| "false"` | – | – | – |
26
+ | `divider` | `"true" \| "false"` | – | – | – |
27
+ | `variant` | `"default" \| "outline" \| "muted"` | `"default"` | – | – |
28
+ | `size` | `"default" \| "sm" \| "xs"` | `"default"` | – | – |
29
+ | `layout` | `"vertical" \| "horizontal"` | `"vertical"` | – | – |
30
+ | `asChild` | `boolean` | – | – | 使用 Slot 模式渲染子元素 |
31
+ | `variant` | `"default" \| "icon" \| "image" \| "cover"` | `"default"` | – | – |
32
+ | `separated` | `boolean` | – | – | 子元素之间自动插入竖向分隔线 |
33
33
 
34
34
  ## 示例
35
35
 
@@ -127,18 +127,10 @@
127
127
  <span className="text-xs text-muted-foreground">3 小时前更新</span>
128
128
  </ItemContent>
129
129
  <ItemActions>
130
- <Button
131
- size="sm"
132
- variant="ghost"
133
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
134
- >
130
+ <Button size="sm" variant="link" className="text-muted-foreground">
135
131
  编辑
136
132
  </Button>
137
- <Button
138
- size="sm"
139
- variant="ghost"
140
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
141
- >
133
+ <Button size="sm" variant="link" className="text-muted-foreground">
142
134
  删除
143
135
  </Button>
144
136
  </ItemActions>
@@ -159,18 +151,10 @@
159
151
  <span className="text-xs text-muted-foreground">1 天前更新</span>
160
152
  </ItemContent>
161
153
  <ItemActions>
162
- <Button
163
- size="sm"
164
- variant="ghost"
165
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
166
- >
154
+ <Button size="sm" variant="link" className="text-muted-foreground">
167
155
  编辑
168
156
  </Button>
169
- <Button
170
- size="sm"
171
- variant="ghost"
172
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
173
- >
157
+ <Button size="sm" variant="link" className="text-muted-foreground">
174
158
  删除
175
159
  </Button>
176
160
  </ItemActions>
@@ -221,26 +205,18 @@ Actions 自动分隔
221
205
  <ItemDescription>点击操作按钮管理</ItemDescription>
222
206
  </ItemContent>
223
207
  <ItemActions separated>
224
- <Button
225
- size="sm"
226
- variant="ghost"
227
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
228
- >
208
+ <Button size="sm" variant="link" className="text-muted-foreground">
229
209
  <PencilIcon />
230
210
  编辑
231
211
  </Button>
232
- <Button
233
- size="sm"
234
- variant="ghost"
235
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
236
- >
212
+ <Button size="sm" variant="link" className="text-muted-foreground">
237
213
  <BookmarkIcon />
238
214
  收藏
239
215
  </Button>
240
216
  <Button
241
217
  size="sm"
242
- variant="ghost"
243
- className="text-destructive hover:bg-transparent hover:text-destructive/80"
218
+ variant="link"
219
+ className="text-destructive hover:text-destructive/80"
244
220
  >
245
221
  <TrashIcon />
246
222
  删除
@@ -314,11 +290,7 @@ Actions 自动分隔
314
290
  </Item>
315
291
  <ItemGroupFooter>
316
292
  <span>共 2 条通知</span>
317
- <Button
318
- size="sm"
319
- variant="ghost"
320
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
321
- >
293
+ <Button size="sm" variant="link" className="text-muted-foreground">
322
294
  查看全部
323
295
  </Button>
324
296
  </ItemGroupFooter>
@@ -344,11 +316,7 @@ Actions 自动分隔
344
316
  </ItemContent>
345
317
  <ItemFooter>
346
318
  <span>截止:2025-01-15</span>
347
- <Button
348
- size="sm"
349
- variant="ghost"
350
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
351
- >
319
+ <Button size="sm" variant="link" className="text-muted-foreground">
352
320
  查看
353
321
  </Button>
354
322
  </ItemFooter>
@@ -51,7 +51,7 @@ export const Default: Story = {
51
51
  <Button
52
52
  size="icon-sm"
53
53
  variant="ghost"
54
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
54
+ className="text-muted-foreground"
55
55
  >
56
56
  <MoreHorizontalIcon />
57
57
  </Button>
@@ -69,7 +69,7 @@ export const Default: Story = {
69
69
  <Button
70
70
  size="icon-sm"
71
71
  variant="ghost"
72
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
72
+ className="text-muted-foreground"
73
73
  >
74
74
  <MoreHorizontalIcon />
75
75
  </Button>
@@ -169,18 +169,10 @@ export const Horizontal: Story = {
169
169
  <span className="text-xs text-muted-foreground">3 小时前更新</span>
170
170
  </ItemContent>
171
171
  <ItemActions>
172
- <Button
173
- size="sm"
174
- variant="ghost"
175
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
176
- >
172
+ <Button size="sm" variant="link" className="text-muted-foreground">
177
173
  编辑
178
174
  </Button>
179
- <Button
180
- size="sm"
181
- variant="ghost"
182
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
183
- >
175
+ <Button size="sm" variant="link" className="text-muted-foreground">
184
176
  删除
185
177
  </Button>
186
178
  </ItemActions>
@@ -201,18 +193,10 @@ export const Horizontal: Story = {
201
193
  <span className="text-xs text-muted-foreground">1 天前更新</span>
202
194
  </ItemContent>
203
195
  <ItemActions>
204
- <Button
205
- size="sm"
206
- variant="ghost"
207
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
208
- >
196
+ <Button size="sm" variant="link" className="text-muted-foreground">
209
197
  编辑
210
198
  </Button>
211
- <Button
212
- size="sm"
213
- variant="ghost"
214
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
215
- >
199
+ <Button size="sm" variant="link" className="text-muted-foreground">
216
200
  删除
217
201
  </Button>
218
202
  </ItemActions>
@@ -261,26 +245,18 @@ export const ActionsSeparated: Story = {
261
245
  <ItemDescription>点击操作按钮管理</ItemDescription>
262
246
  </ItemContent>
263
247
  <ItemActions separated>
264
- <Button
265
- size="sm"
266
- variant="ghost"
267
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
268
- >
248
+ <Button size="sm" variant="link" className="text-muted-foreground">
269
249
  <PencilIcon />
270
250
  编辑
271
251
  </Button>
272
- <Button
273
- size="sm"
274
- variant="ghost"
275
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
276
- >
252
+ <Button size="sm" variant="link" className="text-muted-foreground">
277
253
  <BookmarkIcon />
278
254
  收藏
279
255
  </Button>
280
256
  <Button
281
257
  size="sm"
282
- variant="ghost"
283
- className="text-destructive hover:bg-transparent hover:text-destructive/80"
258
+ variant="link"
259
+ className="text-destructive hover:text-destructive/80"
284
260
  >
285
261
  <TrashIcon />
286
262
  删除
@@ -352,11 +328,7 @@ export const GroupWithHeaderFooter: Story = {
352
328
  </Item>
353
329
  <ItemGroupFooter>
354
330
  <span>共 2 条通知</span>
355
- <Button
356
- size="sm"
357
- variant="ghost"
358
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
359
- >
331
+ <Button size="sm" variant="link" className="text-muted-foreground">
360
332
  查看全部
361
333
  </Button>
362
334
  </ItemGroupFooter>
@@ -381,11 +353,7 @@ export const WithHeaderFooter: Story = {
381
353
  </ItemContent>
382
354
  <ItemFooter>
383
355
  <span>截止:2025-01-15</span>
384
- <Button
385
- size="sm"
386
- variant="ghost"
387
- className="text-muted-foreground hover:bg-transparent hover:text-foreground"
388
- >
356
+ <Button size="sm" variant="link" className="text-muted-foreground">
389
357
  查看
390
358
  </Button>
391
359
  </ItemFooter>
@@ -176,11 +176,15 @@ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
176
176
  // ─── TableBody ──────────────────────────────────────────────────────────────
177
177
 
178
178
  function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
179
- const { striped } = useTableContext();
179
+ const { striped, bordered } = useTableContext();
180
180
  return (
181
181
  <tbody
182
182
  data-slot="table-body"
183
- className={cn(striped && '[&_tr:nth-child(even)]:bg-muted/30', className)}
183
+ className={cn(
184
+ striped && '[&_tr:nth-child(even)]:bg-muted/30',
185
+ bordered && '[&>tr:last-child]:border-b-0',
186
+ className,
187
+ )}
184
188
  {...props}
185
189
  />
186
190
  );
@@ -328,10 +328,7 @@ function TransferPanel({
328
328
 
329
329
  {/* Search */}
330
330
  {showSearch ? (
331
- <div
332
- data-slot="transfer-search"
333
- className="px-2 pt-1.5 pb-2"
334
- >
331
+ <div data-slot="transfer-search" className="px-2 pt-1.5 pb-2">
335
332
  <Input
336
333
  value={search}
337
334
  disabled={disabled}
@@ -404,17 +401,22 @@ function TransferPanel({
404
401
  ) : null}
405
402
  <div className="min-w-0 flex-1">{renderItemContent(item)}</div>
406
403
  {showRemove ? (
407
- <Button
408
- size="icon-sm"
409
- variant="ghost"
410
- className="opacity-0 group-hover/transfer-item:opacity-100"
404
+ <span
405
+ role="button"
406
+ aria-label="移除"
407
+ tabIndex={-1}
408
+ onPointerDown={(e) => {
409
+ e.preventDefault();
410
+ e.stopPropagation();
411
+ }}
411
412
  onClick={(e) => {
412
413
  e.stopPropagation();
413
414
  onItemRemove?.(item.key);
414
415
  }}
416
+ className="inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground opacity-0 transition-colors hover:text-foreground group-hover/transfer-item:opacity-100"
415
417
  >
416
- <X />
417
- </Button>
418
+ <X className="size-3.5" />
419
+ </span>
418
420
  ) : null}
419
421
  </div>
420
422
  );