clinicpal / src /components /ui /glass-toggle-chip.tsx
Vrda's picture
Deploy ClinIcPal frontend
9bc2f29 verified
'use client';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
export interface GlassToggleChipProps {
selected: boolean;
onToggle: () => void;
label: string;
icon?: string;
disabled?: boolean;
className?: string;
}
export function GlassToggleChip({
selected,
onToggle,
label,
icon,
disabled = false,
className,
}: GlassToggleChipProps) {
return (
<motion.button
onClick={onToggle}
disabled={disabled}
className={cn(
'inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full',
'text-sm font-medium',
'border transition-all duration-200',
'focus-ring',
'disabled:opacity-50 disabled:cursor-not-allowed',
selected
? cn(
'bg-[var(--suggestion-bg)] border-[var(--suggestion-border)]',
'text-[var(--suggestion-text)]'
)
: cn(
'bg-[var(--glass-bg-muted)] border-[var(--glass-border-subtle)]',
'text-[var(--foreground)]/60',
'hover:bg-[var(--glass-bg)] hover:text-[var(--foreground)]/80'
),
className
)}
whileHover={{ scale: disabled ? 1 : 1.02 }}
whileTap={{ scale: disabled ? 1 : 0.98 }}
>
{icon && <span>{icon}</span>}
<span>{label}</span>
<motion.span
initial={false}
animate={{
scale: selected ? 1 : 0,
opacity: selected ? 1 : 0,
}}
transition={{ duration: 0.15 }}
className="w-4 h-4 flex items-center justify-center"
>
<svg
className="w-3 h-3 text-[var(--suggestion-accent)]"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={3}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M5 13l4 4L19 7"
/>
</svg>
</motion.span>
</motion.button>
);
}
// ---- Toggle Chip Group ----
export interface ToggleChipGroupProps<T extends string> {
values: T[];
selected: T[];
onChange: (selected: T[]) => void;
getLabel: (value: T) => string;
getIcon?: (value: T) => string | undefined;
disabled?: boolean;
className?: string;
}
export function ToggleChipGroup<T extends string>({
values,
selected,
onChange,
getLabel,
getIcon,
disabled = false,
className,
}: ToggleChipGroupProps<T>) {
const handleToggle = (value: T) => {
if (selected.includes(value)) {
// Don't allow deselecting the last item
if (selected.length > 1) {
onChange(selected.filter((v) => v !== value));
}
} else {
onChange([...selected, value]);
}
};
return (
<div className={cn('flex flex-wrap gap-2', className)}>
{values.map((value) => (
<GlassToggleChip
key={value}
selected={selected.includes(value)}
onToggle={() => handleToggle(value)}
label={getLabel(value)}
icon={getIcon?.(value)}
disabled={disabled}
/>
))}
</div>
);
}