mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(react): orientation-aware buffer styling and slider improvements (#671)
This commit is contained in:
@@ -528,7 +528,16 @@
|
||||
.media-minimal-skin .media-slider__buffer {
|
||||
background-color: oklch(1 0 0 / 0.2);
|
||||
width: var(--media-slider-buffer, 0%);
|
||||
transition: width 0.25s ease-out;
|
||||
transition-duration: 0.25s;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
.media-default-skin .media-slider__buffer[data-orientation="horizontal"] {
|
||||
width: var(--media-slider-buffer);
|
||||
transition-property: width;
|
||||
}
|
||||
.media-default-skin .media-slider__buffer[data-orientation="vertical"] {
|
||||
height: var(--media-slider-buffer);
|
||||
transition-property: height;
|
||||
}
|
||||
|
||||
/* Fill */
|
||||
|
||||
@@ -163,16 +163,33 @@ const SliderTrack = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function S
|
||||
);
|
||||
});
|
||||
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderFill({ className, ...props }, ref) {
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'> & { type?: 'fill' | 'buffer' }>(function SliderFill(
|
||||
{ type = 'fill', className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'absolute rounded-[inherit] pointer-events-none bg-white',
|
||||
'absolute rounded-[inherit] pointer-events-none',
|
||||
{
|
||||
'bg-white': type === 'fill',
|
||||
'bg-white/20 duration-250 ease-out': type === 'buffer',
|
||||
},
|
||||
// Horizontal
|
||||
'data-[orientation=horizontal]:inset-y-0 data-[orientation=horizontal]:left-0 data-[orientation=horizontal]:w-(--media-slider-fill)',
|
||||
`data-[orientation=horizontal]:inset-y-0 data-[orientation=horizontal]:left-0`,
|
||||
{
|
||||
'data-[orientation=horizontal]:w-(--media-slider-fill)': type === 'fill',
|
||||
'data-[orientation=horizontal]:transition-[width] data-[orientation=horizontal]:w-(--media-slider-buffer)':
|
||||
type === 'buffer',
|
||||
},
|
||||
// Vertical
|
||||
'data-[orientation=vertical]:inset-x-0 data-[orientation=vertical]:bottom-0 data-[orientation=vertical]:h-(--media-slider-fill)',
|
||||
`data-[orientation=vertical]:inset-x-0 data-[orientation=vertical]:bottom-0`,
|
||||
{
|
||||
'data-[orientation=vertical]:h-(--media-slider-fill)': type === 'fill',
|
||||
'data-[orientation=vertical]:transition-[height] data-[orientation=vertical]:h-(--media-slider-buffer)':
|
||||
type === 'buffer',
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -387,7 +404,7 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
<TimeSlider.Root render={(props) => <SliderRoot {...props} />}>
|
||||
<TimeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<TimeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
<TimeSlider.Buffer className="absolute inset-y-0 left-0 rounded-[inherit] pointer-events-none bg-white/20 w-(--media-slider-buffer) transition-[width] duration-250 ease-out" />
|
||||
<TimeSlider.Buffer render={(props) => <SliderFill type="buffer" {...props} />} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb render={(props) => <SliderThumb {...props} />} />
|
||||
</TimeSlider.Root>
|
||||
|
||||
@@ -189,7 +189,6 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
<MuteButtonIcon state={state} className="media-icon" />
|
||||
</Button>
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -543,13 +543,16 @@
|
||||
/* Buffer */
|
||||
.media-default-skin .media-slider__buffer {
|
||||
background-color: oklch(1 0 0 / 0.2);
|
||||
transition: width 0.25s ease-out;
|
||||
transition-duration: 0.25s;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
.media-default-skin .media-slider__buffer[data-orientation="horizontal"] {
|
||||
width: var(--media-slider-buffer);
|
||||
transition-property: width;
|
||||
}
|
||||
.media-default-skin .media-slider__buffer[data-orientation="vertical"] {
|
||||
height: var(--media-slider-buffer);
|
||||
transition-property: height;
|
||||
}
|
||||
|
||||
/* Fill */
|
||||
|
||||
@@ -177,16 +177,33 @@ const SliderTrack = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function S
|
||||
);
|
||||
});
|
||||
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderFill({ className, ...props }, ref) {
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'> & { type?: 'fill' | 'buffer' }>(function SliderFill(
|
||||
{ type = 'fill', className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'absolute rounded-[inherit] pointer-events-none bg-white',
|
||||
'absolute rounded-[inherit] pointer-events-none',
|
||||
{
|
||||
'bg-white': type === 'fill',
|
||||
'bg-white/20 duration-250 ease-out': type === 'buffer',
|
||||
},
|
||||
// Horizontal
|
||||
'data-[orientation=horizontal]:inset-y-0 data-[orientation=horizontal]:left-0 data-[orientation=horizontal]:w-(--media-slider-fill)',
|
||||
`data-[orientation=horizontal]:inset-y-0 data-[orientation=horizontal]:left-0`,
|
||||
{
|
||||
'data-[orientation=horizontal]:w-(--media-slider-fill)': type === 'fill',
|
||||
'data-[orientation=horizontal]:transition-[width] data-[orientation=horizontal]:w-(--media-slider-buffer)':
|
||||
type === 'buffer',
|
||||
},
|
||||
// Vertical
|
||||
'data-[orientation=vertical]:inset-x-0 data-[orientation=vertical]:bottom-0 data-[orientation=vertical]:h-(--media-slider-fill)',
|
||||
`data-[orientation=vertical]:inset-x-0 data-[orientation=vertical]:bottom-0`,
|
||||
{
|
||||
'data-[orientation=vertical]:h-(--media-slider-fill)': type === 'fill',
|
||||
'data-[orientation=vertical]:transition-[height] data-[orientation=vertical]:h-(--media-slider-buffer)':
|
||||
type === 'buffer',
|
||||
},
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -394,7 +411,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<TimeSlider.Root render={(props) => <SliderRoot {...props} />}>
|
||||
<TimeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<TimeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
<TimeSlider.Buffer className="absolute inset-y-0 left-0 rounded-[inherit] pointer-events-none bg-white/20 w-(--media-slider-buffer) transition-[width] duration-250 ease-out" />
|
||||
<TimeSlider.Buffer render={(props) => <SliderFill type="buffer" {...props} />} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb render={(props) => <SliderThumb {...props} />} />
|
||||
</TimeSlider.Root>
|
||||
|
||||
@@ -184,7 +184,6 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
<MuteButtonIcon state={state} className="media-icon" />
|
||||
</Button>
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -84,20 +84,20 @@ export function useSlider<State extends SliderState = SliderState>(
|
||||
// Cleanup on unmount.
|
||||
useEffect(() => () => slider.destroy(), [slider]);
|
||||
|
||||
// Force a synchronous re-render after mount so edge thumb alignment
|
||||
// can read DOM measurements from the now-populated element refs.
|
||||
useLayoutEffect(() => {
|
||||
if (rootElementRef.current && thumbElementRef.current) {
|
||||
forceRender();
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Subscribe to interaction state.
|
||||
const interaction = useSnapshot(slider.interaction);
|
||||
|
||||
// Compute derived state from interaction + caller-provided projection.
|
||||
const state = options.computeState(interaction);
|
||||
|
||||
// Force a synchronous re-render after mount so edge thumb alignment
|
||||
// can read DOM measurements from the now-populated element refs.
|
||||
useLayoutEffect(() => {
|
||||
if (state.thumbAlignment === 'edge' && rootElementRef.current && thumbElementRef.current) {
|
||||
forceRender();
|
||||
}
|
||||
}, [state.thumbAlignment]);
|
||||
|
||||
// Adjust CSS var percents for edge thumb alignment when DOM elements are available.
|
||||
const rootEl = rootElementRef.current;
|
||||
const thumbEl = thumbElementRef.current;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useReducer } from 'react';
|
||||
|
||||
export function useForceRender() {
|
||||
|
||||
Reference in New Issue
Block a user