geraete/components/ui/ButtonGroup.tsx
2025-11-24 08:59:14 +01:00

74 lines
2.0 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; // aktuell nur für aria, nicht für Style
return (
<button
key={opt.value}
type="button"
disabled={readOnly}
aria-pressed={isActive}
className={clsx(
// 👇 1:1 aus deinem Snippet:
'relative inline-flex items-center bg-white px-3 py-2 text-sm font-semibold text-gray-900 inset-ring-1 inset-ring-gray-300 hover:bg-gray-50 focus:z-10 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>
);
}