glider variants

This commit is contained in:
Nate Kelley 2025-02-24 15:28:15 -07:00
parent fcd199f93d
commit 7422c25b74
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
2 changed files with 24 additions and 13 deletions

View File

@ -19,6 +19,10 @@ const meta: Meta<typeof Segmented> = {
}, },
value: { value: {
control: 'text' control: 'text'
},
type: {
control: 'radio',
options: ['button', 'track']
} }
}, },
render: (args) => { render: (args) => {

View File

@ -6,6 +6,7 @@ import { motion } from 'framer-motion';
import { cn } from '@/lib/classMerge'; import { cn } from '@/lib/classMerge';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { cva } from 'class-variance-authority'; import { cva } from 'class-variance-authority';
import { useMemoizedFn } from 'ahooks';
export interface SegmentedItem { export interface SegmentedItem {
value: string; value: string;
@ -21,6 +22,7 @@ interface SegmentedProps {
className?: string; className?: string;
size?: 'default' | 'large'; size?: 'default' | 'large';
block?: boolean; block?: boolean;
type?: 'button' | 'track';
} }
const segmentedVariants = cva('relative inline-flex items-center rounded-md', { const segmentedVariants = cva('relative inline-flex items-center rounded-md', {
@ -32,6 +34,10 @@ const segmentedVariants = cva('relative inline-flex items-center rounded-md', {
size: { size: {
default: '', default: '',
large: '' large: ''
},
type: {
button: 'bg-transparent',
track: 'bg-item-select'
} }
} }
}); });
@ -60,19 +66,20 @@ const triggerVariants = cva(
true: '', true: '',
false: '' false: ''
} }
},
compoundVariants: [
{
hovered: true,
selected: false,
className: 'bg-gray-50/50'
} }
]
} }
); );
const gliderVariants = cva('absolute border-border rounded-md border', {
variants: {
type: {
button: 'bg-item-select',
track: 'bg-background'
}
}
});
export const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>( export const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
({ items, value, onChange, className, size = 'default', block = false }, ref) => { ({ items, type = 'track', value, onChange, className, size = 'default', block = false }, ref) => {
const tabRefs = React.useRef<Map<string, HTMLButtonElement>>(new Map()); const tabRefs = React.useRef<Map<string, HTMLButtonElement>>(new Map());
const [selectedValue, setSelectedValue] = useState(value || items[0]?.value); const [selectedValue, setSelectedValue] = useState(value || items[0]?.value);
const [hoveredValue, setHoveredValue] = useState<string | null>(null); const [hoveredValue, setHoveredValue] = useState<string | null>(null);
@ -100,19 +107,19 @@ export const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
} }
}, [selectedValue]); }, [selectedValue]);
const handleValueChange = (newValue: string) => { const handleValueChange = useMemoizedFn((newValue: string) => {
setSelectedValue(newValue); setSelectedValue(newValue);
onChange?.(newValue); onChange?.(newValue);
}; });
return ( return (
<Tabs.Root <Tabs.Root
ref={ref} ref={ref}
value={selectedValue} value={selectedValue}
onValueChange={handleValueChange} onValueChange={handleValueChange}
className={cn(segmentedVariants({ block }), 'bg-item-select', height, className)}> className={cn(segmentedVariants({ block, type }), height, className)}>
<motion.div <motion.div
className={cn('absolute rounded-md border border-gray-200 bg-white', height)} className={cn(gliderVariants({ type }), height)}
initial={false} initial={false}
animate={{ animate={{
width: gliderStyle.width, width: gliderStyle.width,