color-q/src/components/HSegmentedControl.tsx

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>
);
}