77 lines
2.1 KiB
TypeScript
77 lines
2.1 KiB
TypeScript
export type SegmentedControlSize = "content" | "equal";
|
|
export type SegmentedControlWidth = "inline" | "fill";
|
|
|
|
export type SegmentedControlOption<TValue extends string = string> = {
|
|
id: TValue;
|
|
label: React.ReactNode;
|
|
disabled?: boolean;
|
|
title?: string;
|
|
ariaLabel?: string;
|
|
};
|
|
|
|
export type SegmentedControlProps<TValue extends string = string> = {
|
|
options: Array<SegmentedControlOption<TValue>>;
|
|
value: TValue;
|
|
onChange: (value: TValue) => void;
|
|
ariaLabel?: string;
|
|
ariaLabelledBy?: string;
|
|
className?: string;
|
|
optionClassName?: string;
|
|
disabled?: boolean;
|
|
role?: "group" | "tablist";
|
|
size?: SegmentedControlSize;
|
|
width?: SegmentedControlWidth;
|
|
};
|
|
|
|
export default function SegmentedControl<TValue extends string = string>({
|
|
options,
|
|
value,
|
|
onChange,
|
|
ariaLabel,
|
|
ariaLabelledBy,
|
|
className = "",
|
|
optionClassName = "",
|
|
disabled = false,
|
|
role = "tablist",
|
|
size = "content",
|
|
width = "inline"
|
|
}: SegmentedControlProps<TValue>) {
|
|
const rootClassName = [
|
|
"segmented-control",
|
|
`segmented-control-size-${size}`,
|
|
`segmented-control-width-${width}`,
|
|
className
|
|
].filter(Boolean).join(" ");
|
|
const optionRole = role === "tablist" ? "tab" : undefined;
|
|
|
|
return (
|
|
<div className={rootClassName} role={role} aria-label={ariaLabel} aria-labelledby={ariaLabelledBy}>
|
|
{options.map((option) => {
|
|
const selected = option.id === value;
|
|
const buttonClassName = [
|
|
"segmented-control-option",
|
|
selected ? "is-active" : "",
|
|
optionClassName
|
|
].filter(Boolean).join(" ");
|
|
|
|
return (
|
|
<button
|
|
key={option.id}
|
|
type="button"
|
|
role={optionRole}
|
|
aria-selected={role === "tablist" ? selected : undefined}
|
|
aria-pressed={role === "group" ? selected : undefined}
|
|
aria-label={option.ariaLabel}
|
|
title={option.title}
|
|
className={buttonClassName}
|
|
disabled={disabled || option.disabled}
|
|
onClick={() => onChange(option.id)}
|
|
>
|
|
{option.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|