This commit is contained in:
tresabhi committed 2026-10-11 15:44:42 -05:00
1 parent eb0c35d6c6
commit ae2839cb83
14 files changed
+452 -281

No files matched your search

@@ -0,0 +1,54 @@
.options {
position: relative;
display: grid;
border-radius: var(--radius-max);
background-color: var(--gray-3);
.indicator {
position: absolute;
top: 0;
height: 100%;
background-color: var(--accent-3);
outline: var(--outline-focus);
border-radius: var(--radius-max);
transition: var(--duration-brief);
}
.option {
position: relative;
display: flex;
justify-content: center;
padding: var(--space-3) var(--space-4);
color: var(--gray-11);
cursor: pointer;
input {
position: absolute;
width: 1px;
height: 1px;
margin: 0;
opacity: 0;
}
.label {
white-space: nowrap;
}
&:has(input:checked) {
color: var(--gray-12);
}
&:has(input:focus-visible) {
outline: 2px solid var(--accent-8);
outline-offset: 2px;
border-radius: var(--radius-max);
}
}
}
@@ -0,0 +1,51 @@
import { type ReactNode } from "react";
import styles from "./index.module.css";
export interface SegmentedControlOption<Value extends string | number> {
value: Value;
label: ReactNode;
}
export interface SegmentedControlProps<Value extends string | number> {
options: readonly SegmentedControlOption<Value>[];
value: Value;
onValueChange(value: Value): void;
}
export function SegmentedControl<Value extends string | number>({
options,
value,
onValueChange,
}: SegmentedControlProps<Value>) {
const selectedIndex = options.findIndex((option) => option.value === value);
const segmentWidth = 100 / options.length;
return (
<div
className={styles.options}
role="radiogroup"
style={{ gridTemplateColumns: `repeat(${options.length}, 1fr)` }}
>
<div
className={styles.indicator}
style={{
left: `${Math.max(selectedIndex, 0) * segmentWidth}%`,
width: `${segmentWidth}%`,
}}
aria-hidden="true"
/>
{options.map((option) => (
<label className={styles.option} key={option.value}>
<input
type="radio"
value={option.value}
checked={option.value === value}
onChange={() => onValueChange(option.value)}
/>
<span className={styles.label}>{option.label}</span>
</label>
))}
</div>
);
}