feat(Slider): tweak styles for a knob and add slider label
This commit is contained in:
@@ -9,28 +9,43 @@ import {
|
||||
type SliderRootProps,
|
||||
} from 'bits-ui';
|
||||
|
||||
type Props = Omit<SliderRootProps, 'type' | 'onValueChange' | 'onValueCommit'> & {
|
||||
/**
|
||||
* Slider value, numeric.
|
||||
*/
|
||||
value: number;
|
||||
/**
|
||||
* A callback function called when the value changes.
|
||||
* @param newValue - number
|
||||
*/
|
||||
onValueChange?: (newValue: number) => void;
|
||||
/**
|
||||
* A callback function called when the user stops dragging the thumb and the value is committed.
|
||||
* @param newValue - number
|
||||
*/
|
||||
onValueCommit?: (newValue: number) => void;
|
||||
};
|
||||
type Props =
|
||||
& Omit<
|
||||
SliderRootProps,
|
||||
'type' | 'onValueChange' | 'onValueCommit'
|
||||
>
|
||||
& {
|
||||
/**
|
||||
* Slider value, numeric.
|
||||
*/
|
||||
value: number;
|
||||
/**
|
||||
* Optional label displayed inline on the track before the filled range.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* A callback function called when the value changes.
|
||||
* @param newValue - number
|
||||
*/
|
||||
onValueChange?: (newValue: number) => void;
|
||||
/**
|
||||
* A callback function called when the user stops dragging the thumb and the value is committed.
|
||||
* @param newValue - number
|
||||
*/
|
||||
onValueCommit?: (newValue: number) => void;
|
||||
};
|
||||
|
||||
let { value = $bindable(), orientation = 'horizontal', class: className, ...rest }: Props = $props();
|
||||
let {
|
||||
value = $bindable(),
|
||||
orientation = 'horizontal',
|
||||
class: className,
|
||||
label,
|
||||
...rest
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<Slider.Root
|
||||
bind:value={value}
|
||||
bind:value
|
||||
class={cn(
|
||||
'relative flex h-full w-6 touch-none select-none items-center justify-center',
|
||||
orientation === 'horizontal' ? 'w-48 h-6' : 'w-6 h-48',
|
||||
@@ -41,13 +56,23 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
||||
{...rest}
|
||||
>
|
||||
{#snippet children(props)}
|
||||
{#if label && orientation === 'horizontal'}
|
||||
<span class="absolute top-0 left-0 -translate-y-1/2 text-[0.5rem] uppercase text-gray-400">
|
||||
{label}
|
||||
</span>
|
||||
{/if}
|
||||
<span
|
||||
{...props}
|
||||
class={cn('relative bg-background-muted rounded-full', orientation === 'horizontal' ? 'w-full h-px' : 'h-full w-px')}
|
||||
class={cn(
|
||||
'relative bg-background-muted rounded-full',
|
||||
orientation === 'horizontal' ? 'w-full h-px' : 'h-full w-px',
|
||||
)}
|
||||
>
|
||||
<!-- Filled range with NO transition -->
|
||||
<Slider.Range
|
||||
class={cn('absolute bg-foreground rounded-full', orientation === 'horizontal' ? 'h-full' : 'w-full')}
|
||||
class={cn(
|
||||
'absolute bg-foreground rounded-full',
|
||||
orientation === 'horizontal' ? 'h-full' : 'w-full',
|
||||
)}
|
||||
/>
|
||||
|
||||
<Slider.Thumb
|
||||
@@ -56,27 +81,32 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
||||
'group/thumb relative block',
|
||||
'size-2',
|
||||
orientation === 'horizontal' ? '-top-1' : '-left-1',
|
||||
'rounded-sm',
|
||||
'rounded-full',
|
||||
'bg-foreground',
|
||||
// Glow shadow
|
||||
'shadow-[0_0_6px_rgba(0,0,0,0.4)]',
|
||||
// Smooth transitions only for size/position
|
||||
'duration-200 ease-out',
|
||||
orientation === 'horizontal' ? 'transition-[height,top,left,box-shadow]' : 'transition-[width,top,left,box-shadow]',
|
||||
orientation === 'horizontal'
|
||||
? 'transition-[height,top,left,box-shadow]'
|
||||
: 'transition-[width,top,left,box-shadow]',
|
||||
// Hover: bigger glow
|
||||
'hover:shadow-[0_0_10px_rgba(0,0,0,0.5)]',
|
||||
orientation === 'horizontal' ? 'hover:size-3 hover:-top-[5.5px]' : 'hover:size-3 hover:-left-[5.5px]',
|
||||
orientation === 'horizontal'
|
||||
? 'hover:size-3 hover:-top-[5.5px]'
|
||||
: 'hover:size-3 hover:-left-[5.5px]',
|
||||
// Active: smaller glow
|
||||
'active:shadow-[0_0_4px_rgba(0,0,0,0.3)]',
|
||||
orientation === 'horizontal' ? 'active:h-2.5 active:-top-[4.5px]' : 'active:w-2.5 active:-left-[4.5px]',
|
||||
orientation === 'horizontal'
|
||||
? 'active:h-2.5 active:-top-[4.5px]'
|
||||
: 'active:w-2.5 active:-left-[4.5px]',
|
||||
'focus:outline-none',
|
||||
'cursor-grab active:cursor-grabbing',
|
||||
)}
|
||||
>
|
||||
<!-- Soft glow on hover -->
|
||||
<div
|
||||
class="
|
||||
absolute inset-0 rounded-sm
|
||||
absolute inset-0 rounded-full
|
||||
bg-background-20
|
||||
opacity-0 group-hover/thumb:opacity-100
|
||||
transition-opacity duration-200
|
||||
@@ -84,11 +114,12 @@ let { value = $bindable(), orientation = 'horizontal', class: className, ...rest
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Value label -->
|
||||
<span
|
||||
class={cn(
|
||||
'absolute',
|
||||
orientation === 'horizontal' ? '-top-8 left-1/2 -translate-x-1/2' : 'left-5 top-1/2 -translate-y-1/2',
|
||||
orientation === 'horizontal'
|
||||
? '-top-8 left-1/2 -translate-x-1/2'
|
||||
: 'left-5 top-1/2 -translate-y-1/2',
|
||||
'px-1.5 py-0.5 rounded-md',
|
||||
'bg-foreground/90 backdrop-blur-sm',
|
||||
'font-mono text-[0.625rem] font-medium text-background',
|
||||
|
||||
Reference in New Issue
Block a user