geraete/components/ui/ButtonGroup.tsx
2025-11-26 08:02:48 +01:00

77 lines
2.1 KiB
TypeScript

// components/ui/ButtonGroup.tsx
'use client';
import * as React from 'react';
import clsx from 'clsx';
export type ButtonGroupOption = {
value: string;
label: React.ReactNode;
icon?: React.ReactNode;
};
type ButtonGroupProps = {
options: ButtonGroupOption[];
value: string;
onChange?: (next: string) => void;
className?: string;
/** Wenn true: nur Anzeige, keine Klicks */
readOnly?: boolean;
};
export default function ButtonGroup({
options,
value,
onChange,
className,
readOnly = false,
}: ButtonGroupProps) {
return (
<span
className={clsx(
'isolate inline-flex rounded-md shadow-xs dark:shadow-none',
className,
)}
>
{options.map((opt, idx) => {
const isFirst = idx === 0;
const isLast = idx === options.length - 1;
const isActive = opt.value === value;
return (
<button
key={opt.value}
type="button"
disabled={readOnly}
aria-pressed={isActive}
className={clsx(
'relative inline-flex items-center px-3 py-2 text-sm font-semibold focus:z-10 inset-ring-1',
// 🔹 aktiver Button
isActive
? 'bg-indigo-600 text-white inset-ring-indigo-600 hover:bg-indigo-500 dark:bg-indigo-500 dark:text-white dark:inset-ring-indigo-400'
: 'bg-white text-gray-900 inset-ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:inset-ring-gray-700 dark:hover:bg-white/20',
!isFirst && '-ml-px',
isFirst && 'rounded-l-md',
isLast && 'rounded-r-md',
readOnly && 'cursor-default opacity-90',
)}
onClick={() => {
if (readOnly) return;
if (!onChange) return;
if (opt.value === value) return;
onChange(opt.value);
}}
>
{opt.icon && (
<span className="mr-1.5 inline-flex items-center">
{opt.icon}
</span>
)}
<span>{opt.label}</span>
</button>
);
})}
</span>
);
}