67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
import { type ComponentPropsWithoutRef, useEffect, useRef } from 'react';
|
|
import { ToggleGroup as RToggleGroup } from 'radix-ui';
|
|
import { cx } from '../utils';
|
|
|
|
type SegProps = Omit<
|
|
ComponentPropsWithoutRef<typeof RToggleGroup.Root>,
|
|
'type' | 'onValueChange' | 'defaultValue' | 'value'
|
|
> & {
|
|
value: string;
|
|
defaultValue?: string;
|
|
onValueChange: (v: string) => void;
|
|
items: Array<{ value: string; label: string }>;
|
|
};
|
|
|
|
export const SegmentedControl = ({
|
|
value,
|
|
onValueChange,
|
|
items,
|
|
className,
|
|
...props
|
|
}: SegProps) => {
|
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
const thumbRef = useRef<HTMLSpanElement>(null);
|
|
const initialized = useRef(false);
|
|
|
|
useEffect(() => {
|
|
const root = rootRef.current;
|
|
const thumb = thumbRef.current;
|
|
if (!root || !thumb) return;
|
|
const selected = root.querySelector<HTMLElement>('[data-state="on"]');
|
|
if (!selected) return;
|
|
|
|
if (!initialized.current) {
|
|
thumb.style.transition = 'none';
|
|
}
|
|
thumb.style.transform = `translateX(${selected.offsetLeft}px)`;
|
|
thumb.style.width = `${selected.offsetWidth}px`;
|
|
if (!initialized.current) {
|
|
thumb.getBoundingClientRect();
|
|
thumb.style.transition = '';
|
|
initialized.current = true;
|
|
}
|
|
}, [value]);
|
|
|
|
return (
|
|
<RToggleGroup.Root
|
|
ref={rootRef}
|
|
type="single"
|
|
className={cx('modern-sk-seg', className)}
|
|
value={value}
|
|
onValueChange={(v) => v && onValueChange(v)}
|
|
{...props}
|
|
>
|
|
<span ref={thumbRef} className="modern-sk-seg__thumb" aria-hidden />
|
|
{items.map((it) => (
|
|
<RToggleGroup.Item
|
|
key={it.value}
|
|
value={it.value}
|
|
className="modern-sk-seg__item"
|
|
>
|
|
{it.label}
|
|
</RToggleGroup.Item>
|
|
))}
|
|
</RToggleGroup.Root>
|
|
);
|
|
};
|