forked from cerc-io/snowballtools-base
* ⚡️ feat: create segmented controls component * 📝 docs: add segmented controls component to example page * ♻️ refactor: put the icon size to icon theme * 🐛 fix: remove `value` from `useCallback` dependency
94 lines
2.0 KiB
TypeScript
94 lines
2.0 KiB
TypeScript
import React, {
|
|
useCallback,
|
|
type ComponentPropsWithoutRef,
|
|
type ReactNode,
|
|
} from 'react';
|
|
|
|
import {
|
|
SegmentedControlItem,
|
|
type SegmentedControlItemProps,
|
|
} from './SegmentedControlItem';
|
|
import {
|
|
segmentedControlsTheme,
|
|
type SegmentedControlsVariants,
|
|
} from './SegmentedControls.theme';
|
|
|
|
/**
|
|
* Represents an option for a segmented control.
|
|
*/
|
|
export interface SegmentedControlsOption
|
|
extends Omit<SegmentedControlItemProps, 'children'> {
|
|
/**
|
|
* The label of the item.
|
|
*/
|
|
label: ReactNode;
|
|
/**
|
|
* The value of the item.
|
|
*
|
|
*/
|
|
value: string;
|
|
}
|
|
|
|
/**
|
|
* Represents the props for the SegmentedControls component.
|
|
*/
|
|
export interface SegmentedControlsProps<T extends string = string>
|
|
extends Omit<ComponentPropsWithoutRef<'div'>, 'onChange'>,
|
|
SegmentedControlsVariants {
|
|
/**
|
|
* An array of options for a segmented control component.
|
|
*/
|
|
options: SegmentedControlsOption[];
|
|
/**
|
|
* An optional string value.
|
|
*/
|
|
value?: T;
|
|
/**
|
|
* Optional callback function to handle changes in state.
|
|
*/
|
|
onChange?: (v: T) => void;
|
|
}
|
|
|
|
/**
|
|
* A component that renders segmented controls with customizable options.
|
|
*/
|
|
export function SegmentedControls<T extends string = string>({
|
|
className,
|
|
options,
|
|
value,
|
|
type,
|
|
size,
|
|
onChange,
|
|
...props
|
|
}: SegmentedControlsProps<T>) {
|
|
const { parent } = segmentedControlsTheme({ size, type });
|
|
|
|
/**
|
|
* Handles the change event for a given option.
|
|
*/
|
|
const handleChange = useCallback(
|
|
(option: T) => {
|
|
if (!option) return;
|
|
onChange?.(option);
|
|
},
|
|
[onChange],
|
|
);
|
|
|
|
return (
|
|
<div {...props} className={parent({ className })}>
|
|
{options.map((option, index) => (
|
|
<SegmentedControlItem
|
|
key={index}
|
|
active={value === option.value}
|
|
size={size}
|
|
type={type}
|
|
onClick={() => handleChange(option.value as T)}
|
|
{...option}
|
|
>
|
|
{option.label}
|
|
</SegmentedControlItem>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|