feat(add): 新增 ButtonGroup 组件

This commit is contained in:
Jeff Gu committed 2026-04-14 17:17:59 +08:00
1 parent 3dc1fed1c1
commit c915efe4e9
3 files changed
+76 -21

No files matched your search

+1
View File
@@ -33,3 +33,4 @@ lerna-debug.log*
public*/
bun.lock
*.local.jsonc
docs/
@@ -0,0 +1,63 @@
import * as React from "react";
import { Button, type ButtonProps } from "@/components/ui/button";
import { cn } from "@/lib/utils";
type ButtonGroupValue = string | number;
export interface ButtonGroupOption<T extends ButtonGroupValue = ButtonGroupValue> {
label: React.ReactNode;
value: T;
disabled?: boolean;
}
export interface ButtonGroupProps<T extends ButtonGroupValue = ButtonGroupValue>
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
value: T;
options: readonly ButtonGroupOption<T>[];
onValueChange: (value: T) => void;
size?: ButtonProps["size"];
activeVariant?: ButtonProps["variant"];
inactiveVariant?: ButtonProps["variant"];
buttonClassName?: string;
}
export function ButtonGroup<T extends ButtonGroupValue = ButtonGroupValue>({
value,
options,
onValueChange,
size = "sm",
activeVariant = "default",
inactiveVariant = "outline",
className,
buttonClassName,
...props
}: ButtonGroupProps<T>) {
return (
<div
{...props}
role="radiogroup"
className={cn("inline-flex flex-wrap rounded-md border border-border", className)}
>
{options.map((option) => {
const isActive = Object.is(option.value, value);
return (
<Button
key={String(option.value)}
type="button"
role="radio"
aria-checked={isActive}
variant={isActive ? activeVariant : inactiveVariant}
size={size}
disabled={option.disabled}
onClick={() => onValueChange(option.value)}
className={cn("border-0", size === "sm" && "h-8", buttonClassName)}
>
{option.label}
</Button>
);
})}
</div>
);
}
+12 -21
View File
@@ -4,6 +4,7 @@ import { Bot, Check, FileText, Globe, Shield, SlidersHorizontal } from "lucide-r
import { apiClient } from "@/api/client";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
@@ -366,27 +367,17 @@ export function SystemSettings() {
</div>
</div>
<div className="flex-1" />
<div className="inline-flex flex-wrap rounded-md border">
{PRECISION_OPTIONS.map((option) => {
const isActive = normalizedSystemConfig.displayDecimals === option.value;
return (
<Button
key={option.value}
type="button"
variant={isActive ? "default" : "outline"}
onClick={() =>
setSystemConfig((current) => ({
...current,
displayDecimals: option.value,
}))
}
className="h-8 border-0"
>
{option.label}
</Button>
);
})}
</div>
<ButtonGroup
aria-label="显示金额精度"
value={normalizedSystemConfig.displayDecimals}
options={PRECISION_OPTIONS}
onValueChange={(value) =>
setSystemConfig((current) => ({
...current,
displayDecimals: value,
}))
}
/>
</section>
<div className="h-px bg-border" />