feat(react): add slider preview component (#710)

This commit is contained in:
rahim
2026-03-04 19:38:56 -08:00
committed by GitHub
parent a6405e9278
commit db7569711e
7 changed files with 308 additions and 1 deletions
@@ -15,3 +15,24 @@ export function getTimeSliderCSSVars(state: TimeSliderState): Record<string, str
[SliderCSSVars.buffer]: `${state.bufferPercent.toFixed(3)}%`,
};
}
// ---------------------------------------------------------------------------
// Slider Preview
// ---------------------------------------------------------------------------
export type SliderPreviewOverflow = 'clamp' | 'visible';
/** Compute structural positioning styles for a slider preview element. */
export function getSliderPreviewStyle(width: number, overflow: SliderPreviewOverflow) {
const halfWidth = width / 2;
return {
position: 'absolute',
left:
overflow === 'visible'
? `calc(var(${SliderCSSVars.pointer}) - ${halfWidth}px)`
: `min(max(0px, calc(var(${SliderCSSVars.pointer}) - ${halfWidth}px)), calc(100% - ${width}px))`,
width: 'max-content',
pointerEvents: 'none',
};
}
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { createSliderState, createTimeSliderState } from '../../tests/test-helpers';
import { getSliderCSSVars, getTimeSliderCSSVars } from '../slider-css-vars';
import { getSliderCSSVars, getSliderPreviewStyle, getTimeSliderCSSVars } from '../slider-css-vars';
describe('getSliderCSSVars', () => {
it('returns fill and pointer CSS vars with 3-decimal precision', () => {
@@ -55,3 +55,36 @@ describe('getTimeSliderCSSVars', () => {
expect(vars['--media-slider-buffer']).toBe('33.333%');
});
});
describe('getSliderPreviewStyle', () => {
it('returns structural positioning properties', () => {
const style = getSliderPreviewStyle(100, 'clamp');
expect(style.position).toBe('absolute');
expect(style.width).toBe('max-content');
expect(style.pointerEvents).toBe('none');
});
it('clamps left within slider bounds by default', () => {
const style = getSliderPreviewStyle(100, 'clamp');
expect(style.left).toContain('min(');
expect(style.left).toContain('max(');
expect(style.left).toContain('var(--media-slider-pointer)');
expect(style.left).toContain('50px');
expect(style.left).toContain('100px');
});
it('uses unclamped calc when overflow is visible', () => {
const style = getSliderPreviewStyle(100, 'visible');
expect(style.left).toBe('calc(var(--media-slider-pointer) - 50px)');
expect(style.left).not.toContain('min(');
});
it('handles zero width', () => {
const style = getSliderPreviewStyle(0, 'clamp');
expect(style.left).toContain('0px');
});
});