73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import cx from 'clsx';
|
|
import { isEqual, isMap, isNil } from 'lodash-es';
|
|
import { useCallback, useRef, useState } from 'react';
|
|
import type { Option } from '../models';
|
|
import styles from './HSegmentedControl.module.css';
|
|
|
|
type HSegmentedControlProps = {
|
|
name?: string;
|
|
defaultValue?: Option['value'];
|
|
options?: Option[];
|
|
value?: Option['value'];
|
|
onChange?: (value: Option['value']) => void;
|
|
extendClassName?: HTMLDivElement['className'];
|
|
};
|
|
|
|
export function HSegmentedControl({
|
|
name,
|
|
defaultValue,
|
|
options = [],
|
|
value,
|
|
onChange,
|
|
extendClassName,
|
|
}: HSegmentedControlProps) {
|
|
const [selected, setSelected] = useState(
|
|
value ??
|
|
defaultValue ??
|
|
(isMap(options[0]) ? options[0].get('value') : options[0].value) ??
|
|
null,
|
|
);
|
|
const [sliderPosition, setSliderPosition] = useState(0);
|
|
const [sliderWidth, setSliderWidth] = useState(0);
|
|
const sliderRef = useRef<HTMLDivElement>(null);
|
|
const optionsRef = useRef<HTMLDivElement[]>([]);
|
|
|
|
const handleSelectAction = useCallback((option: Option['value'], index: number) => {
|
|
setSelected(option);
|
|
onChange?.(option);
|
|
if (optionsRef.current && optionsRef.current.length > index) {
|
|
const optionElement = optionsRef.current[index];
|
|
setSliderPosition(optionElement.offsetLeft);
|
|
setSliderWidth(optionElement.offsetWidth);
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<div className={cx(styles.segmented_control, extendClassName)}>
|
|
<div className={styles.options}>
|
|
{options.map((option, index) => {
|
|
const label = isMap(option) ? option.get('label') : option.label;
|
|
const value = isMap(option) ? option.get('value') : option.value;
|
|
return (
|
|
<div
|
|
key={`${index}_${value}`}
|
|
className={cx(styles.option, isEqual(selected, value) && styles.selected)}
|
|
ref={(el) => (optionsRef.current[index] = el!)}
|
|
onClick={() => handleSelectAction(value, index)}>
|
|
{label}
|
|
</div>
|
|
);
|
|
})}
|
|
{!isNil(selected) && (
|
|
<div
|
|
className={styles.slider}
|
|
ref={sliderRef}
|
|
style={{ left: `${sliderPosition}px`, width: `${sliderWidth}px` }}
|
|
/>
|
|
)}
|
|
</div>
|
|
{!isNil(name) && <input type="hidden" name={name} value={selected} />}
|
|
</div>
|
|
);
|
|
}
|