import clsx from 'clsx'; import React from 'react'; export interface SegmentedControlOption { value: T; label: React.ReactNode; // Optional accessible label when `label` is a non-text node (icon, badge…). ariaLabel?: string; // Per-option disable, in addition to the group-level `disabled` prop. disabled?: boolean; } interface SegmentedControlProps { options: ReadonlyArray>; value: T; onChange: (value: T) => void; // Group-level accessible name (rendered as `aria-label` on the wrapper). ariaLabel?: string; // Group-level disable. Per-option `disabled` is OR'd with this. disabled?: boolean; size?: 'sm' | 'md'; // Stretch segments to fill the container; otherwise they hug their content. fullWidth?: boolean; className?: string; } // iOS-style segmented control: a subtle track holds N equally-weighted // segments. The active one rises on top as a filled pill with a slight // shadow; inactive ones are flat, transparent, and slightly muted so the // group reads as a single control rather than a row of separate buttons. // // Generic over the value type so callers preserve number / string / enum // semantics: // // // options={[{ value: 1, label: '1 day' }, ...]} // value={days} // onChange={setDays} // /> const SegmentedControl = ({ options, value, onChange, ariaLabel, disabled, size = 'sm', fullWidth = false, className, }: SegmentedControlProps) => { const sizeClasses = size === 'md' ? 'px-4 py-1.5 text-sm' : 'px-3 py-1 text-sm'; return (
{options.map((option) => { const selected = option.value === value; const optionDisabled = !!disabled || !!option.disabled; return ( ); })}
); }; export default SegmentedControl;