945780def8
feat: optimize dashboard/search UX and simplify extension architecture Redesign Dashboard with compact tool grid and recently used tools Improve TopBar search UX with Cmd/Ctrl+K shortcut and better history navigation Reorganize project structure into src/ Migrate i18n from react-i18next to chrome.i18n Remove runtime language switch and settings page Remove HTML/Markdown conversion tools Clean up unused code, dead animations, redundant comments, and imports Improve component consistency with shadcn/ui patterns Replace hardcoded strings/colors with i18n tokens and theme tokens Add comprehensive project documentation and coding standards Fix CI artifact upload workflow and multiple TypeScript/test issues Includes various refactors, UI polish, i18n cleanup, CI improvements, and maintenance updates across the codebase.
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
import React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export interface SwitchOption<T extends string | number = string> {
|
|
value: T;
|
|
label: React.ReactNode;
|
|
}
|
|
|
|
export interface SwitchButtonGroupProps<T extends string | number = string> extends Omit<
|
|
React.HTMLAttributes<HTMLDivElement>,
|
|
'onChange'
|
|
> {
|
|
value: T;
|
|
options: SwitchOption<T>[];
|
|
onChange: (value: T) => void;
|
|
size?: 'small' | 'medium' | 'large';
|
|
buttonClassName?: string;
|
|
}
|
|
|
|
export default function SwitchButtonGroup<T extends string | number = string>({
|
|
value,
|
|
options,
|
|
onChange,
|
|
size = 'medium',
|
|
className,
|
|
buttonClassName,
|
|
...props
|
|
}: SwitchButtonGroupProps<T>) {
|
|
const sizeClasses = {
|
|
small: 'text-xs h-8 px-2 py-1 rounded-md',
|
|
medium: 'text-sm h-9 px-3 py-1.5 rounded-md',
|
|
large: 'text-base h-11 px-4 py-2 rounded-lg',
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'inline-flex w-full items-center justify-center rounded-lg bg-muted text-muted-foreground p-1',
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{options.map((option) => {
|
|
const isSelected = value === option.value;
|
|
|
|
return (
|
|
<button
|
|
key={option.value}
|
|
type="button"
|
|
onClick={() => onChange(option.value)}
|
|
className={cn(
|
|
'flex-1 inline-flex items-center justify-center font-medium whitespace-nowrap transition-all',
|
|
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
'disabled:pointer-events-none disabled:opacity-50',
|
|
sizeClasses[size],
|
|
isSelected
|
|
? 'bg-background text-foreground shadow-sm font-semibold animate-in fade-in-50 zoom-in-95 duration-150'
|
|
: 'hover:bg-background/50 hover:text-foreground/80',
|
|
buttonClassName,
|
|
)}
|
|
>
|
|
{option.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|