import { clsx } from 'clsx'; import { createRef, useEffect, useRef, useState } from 'react'; import Body from '../body'; import { Typography } from '../common'; type SegmentBase = { id: string; label: string; value: string }; type Segment = SegmentBase & { controls?: never }; type SegmentWithControls = SegmentBase & { controls: string }; export type Segments = readonly Segment[] | readonly SegmentWithControls[]; type SegmentedControlPropsBase = { name: string; value: string; mode: 'input' | 'view'; onChange: (value: string) => void; }; type SegmentedControlViewProps = { mode: 'view'; segments: readonly SegmentWithControls[]; }; type SegmentedControlInputProps = { mode: 'input'; segments: readonly Segment[]; }; export type SegmentedControlProps = SegmentedControlPropsBase & (SegmentedControlViewProps | SegmentedControlInputProps); const SegmentedControl = ({ name, value, mode = 'input', segments, onChange, }: SegmentedControlProps) => { const [animate, setAnimate] = useState(false); const segmentsRef = useRef(null); if (segments.length > 3) { throw new Error( 'SegmentedControl only supports up to 3 segments. Please refer to: https://wise.design/components/segmented-control', ); } const segmentsWithRefs = segments.map((segment) => ({ ...segment, ref: createRef(), })); const updateSegmentPosition = () => { const selectedSegmentRef = segmentsWithRefs.find((segment) => segment.value === value)?.ref; // We grab the active segments style object from the ref // and set the css variables to the selected segments width and x position. // This is so we can animate the highlight to the selected segment if (selectedSegmentRef?.current && segmentsRef.current) { const { style } = segmentsRef.current; style.setProperty('--segment-highlight-width', `${selectedSegmentRef.current.offsetWidth}px`); style.setProperty('--segment-highlight-x', `${selectedSegmentRef.current.offsetLeft}px`); } }; useEffect(() => { setAnimate(true); updateSegmentPosition(); const handleWindowSizeChange = () => { setAnimate(false); updateSegmentPosition(); }; window.addEventListener('resize', handleWindowSizeChange); return () => { window.removeEventListener('resize', handleWindowSizeChange); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [segmentsWithRefs, value]); return (
{segmentsWithRefs.map((segment) => { const onSelect = () => { setAnimate(true); onChange(segment.value); }; return mode === 'input' ? ( ) : ( ); })}
); }; export default SegmentedControl;