This commit is contained in:
2026-05-31 18:09:55 +03:00
parent 22afa7e1a5
commit a5d2742c7c
6 changed files with 123 additions and 33 deletions
+47 -20
View File
@@ -1,4 +1,4 @@
import { type ComponentPropsWithoutRef } from 'react';
import { type ComponentPropsWithoutRef, useEffect, useRef } from 'react';
import { ToggleGroup as RToggleGroup } from 'radix-ui';
import { cx } from '../utils';
@@ -18,22 +18,49 @@ export const SegmentedControl = ({
items,
className,
...props
}: SegProps) => (
<RToggleGroup.Root
type="single"
className={cx('modern-sk-seg', className)}
value={value}
onValueChange={(v) => v && onValueChange(v)}
{...props}
>
{items.map((it) => (
<RToggleGroup.Item
key={it.value}
value={it.value}
className="modern-sk-seg__item"
>
{it.label}
</RToggleGroup.Item>
))}
</RToggleGroup.Root>
);
}: 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>
);
};
+56 -8
View File
@@ -1,14 +1,62 @@
import { type ComponentPropsWithoutRef } from 'react';
import { Slider as RSlider } from 'radix-ui';
export const Slider = (props: ComponentPropsWithoutRef<typeof RSlider.Root>) => (
<RSlider.Root className="modern-sk-slider" {...props}>
<RSlider.Track className="modern-sk-slider__track">
<RSlider.Range className="modern-sk-slider__range" />
</RSlider.Track>
<RSlider.Thumb className="modern-sk-slider__thumb" aria-label="Value" />
</RSlider.Root>
);
type Step = { value: number; label?: string };
type SliderProps = ComponentPropsWithoutRef<typeof RSlider.Root> & {
steps?: number | Step[];
};
function resolveSteps(steps: number | Step[], min: number, max: number): Step[] {
if (Array.isArray(steps)) return steps;
if (steps < 2) return [];
return Array.from({ length: steps }, (_, i) => ({
value: min + (i / (steps - 1)) * (max - min),
}));
}
export const Slider = ({ steps, min = 0, max = 100, ...props }: SliderProps) => {
const resolved = steps != null ? resolveSteps(steps, min, max) : [];
const hasSteps = resolved.length > 0;
return (
<RSlider.Root
className={`modern-sk-slider${hasSteps ? ' modern-sk-slider--has-steps' : ''}`}
min={min}
max={max}
{...props}
>
<RSlider.Track className="modern-sk-slider__track">
<RSlider.Range className="modern-sk-slider__range" />
{hasSteps && resolved.map((step) => (
<div
key={step.value}
className="modern-sk-slider__step-dot"
aria-hidden
style={{ left: `${((step.value - min) / (max - min)) * 100}%` }}
/>
))}
</RSlider.Track>
<RSlider.Thumb className="modern-sk-slider__thumb" aria-label="Value" />
{hasSteps && (
<div className="modern-sk-slider__steps" aria-hidden>
{resolved.map((step) => (
<div
key={step.value}
className="modern-sk-slider__step"
style={{ left: `${((step.value - min) / (max - min)) * 100}%` }}
>
<div className="modern-sk-slider__step-tick" />
{step.label != null && (
<span className="modern-sk-slider__step-label">{step.label}</span>
)}
</div>
))}
</div>
)}
</RSlider.Root>
);
};
export const Stepper = ({
onDecrement,
+1 -1
View File
@@ -9,7 +9,7 @@ type TooltipProps = {
open?: boolean;
defaultOpen?: boolean;
onOpenChange?: (o: boolean) => void;
} & Omit<ComponentPropsWithoutRef<typeof RTooltip.Content>, 'children'>;
} & Omit<ComponentPropsWithoutRef<typeof RTooltip.Content>, 'children' | 'content'>;
export const Tooltip = ({
content,