@zmzai/theme 0.2.1 → 0.2.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": "@zmzai/theme",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "zmzai 全品牌设计系统 — Radix UI + framer-motion + Tailwind v4 + MiSans",
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import { forwardRef, type ButtonHTMLAttributes } from "react";
4
+ import { cn } from "../../utils/cn";
5
+
6
+ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "ref"> {
7
+ /** 尺寸:sm=h-6 md=h-7 lg=h-8(密集工具条场景,不做 hover 缩放) */
8
+ size?: "sm" | "md" | "lg";
9
+ /** 视觉:ghost=透明 hover 背景;quiet=无 hover 背景只有颜色变化 */
10
+ tone?: "ghost" | "quiet";
11
+ label?: string;
12
+ }
13
+
14
+ /**
15
+ * IconButton — 密集场景的纯图标按钮。
16
+ *
17
+ * 与 Button 的区别:不做 framer-motion hover 缩放(工具条里会跳),
18
+ * 尺寸更小(6/7/8),tone=quiet 时连背景都没有(纯颜色 hover)。
19
+ *
20
+ * @example
21
+ * <IconButton label="重命名"><Icon name="edit" size={12} /></IconButton>
22
+ */
23
+ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
24
+ ({ className, size = "md", tone = "ghost", label, type = "button", ...props }, ref) => {
25
+ return (
26
+ <button
27
+ ref={ref}
28
+ type={type}
29
+ aria-label={label}
30
+ title={label}
31
+ className={cn(
32
+ "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-sm text-ink-3 transition-colors disabled:pointer-events-none disabled:opacity-40",
33
+ size === "sm" && "h-6 w-6",
34
+ size === "md" && "h-7 w-7",
35
+ size === "lg" && "h-8 w-8",
36
+ tone === "ghost" && "hover:bg-surface-2 hover:text-ink",
37
+ tone === "quiet" && "hover:text-ink",
38
+ className,
39
+ )}
40
+ {...props}
41
+ />
42
+ );
43
+ },
44
+ );
45
+
46
+ IconButton.displayName = "IconButton";
@@ -0,0 +1,2 @@
1
+ export { IconButton } from "./IconButton";
2
+ export type { IconButtonProps } from "./IconButton";
@@ -13,6 +13,8 @@ export * from "./tooltip";
13
13
  export * from "./dialog";
14
14
  export * from "./dropdown-menu";
15
15
  export * from "./select";
16
+ export * from "./icon-button";
17
+ export * from "./tabs";
16
18
 
17
19
  // === Interactive Enhancement ===
18
20
  export * from "./magnetic-button";
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import { cn } from "../../utils/cn";
4
+
5
+ export type TabItem = { value: string; label: React.ReactNode; count?: number };
6
+
7
+ export interface TabsProps {
8
+ items: TabItem[];
9
+ value: string;
10
+ onValueChange: (value: string) => void;
11
+ className?: string;
12
+ }
13
+
14
+ /**
15
+ * Tabs — 下划线激活态的水平标签栏(锐角风)。
16
+ *
17
+ * @example
18
+ * <Tabs items={[{value:"a",label:"产物",count:2},{value:"b",label:"改动"}]} value={tab} onValueChange={setTab} />
19
+ */
20
+ export function Tabs({ items, value, onValueChange, className }: TabsProps) {
21
+ return (
22
+ <div className={cn("flex gap-0 border-b border-line", className)} role="tablist">
23
+ {items.map((item) => {
24
+ const active = item.value === value;
25
+ return (
26
+ <button
27
+ key={item.value}
28
+ type="button"
29
+ role="tab"
30
+ aria-selected={active}
31
+ className={cn(
32
+ "cursor-pointer border-b-2 px-3 py-2.5 text-xs font-medium transition-colors",
33
+ active ? "border-ink text-ink" : "border-transparent text-ink-3 hover:text-ink-2",
34
+ )}
35
+ onClick={() => onValueChange(item.value)}
36
+ >
37
+ {item.label}
38
+ {typeof item.count === "number" && (
39
+ <span className="ml-1 rounded-full bg-surface-2 px-1.5 text-[10px]">{item.count}</span>
40
+ )}
41
+ </button>
42
+ );
43
+ })}
44
+ </div>
45
+ );
46
+ }
@@ -0,0 +1,2 @@
1
+ export { Tabs } from "./Tabs";
2
+ export type { TabItem, TabsProps } from "./Tabs";