mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat: add slider preview thumbnails (#935)
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
transition-timing-function: ease-out;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
touch-action: manipulation;
|
||||
|
||||
&:focus-visible {
|
||||
outline-color: oklch(62.3% 0.214 259.815);
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
object-fit: cover;
|
||||
transition: opacity 0.25s;
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
|
||||
&:not([data-visible]) {
|
||||
opacity: 0;
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
color: inherit;
|
||||
overflow: visible;
|
||||
transition-property: transform, scale, opacity, filter;
|
||||
transition-duration: 200ms;
|
||||
transition-duration: 150ms;
|
||||
|
||||
&[data-starting-style],
|
||||
&[data-ending-style] {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transform: scale(0.5);
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
@@ -49,5 +49,5 @@
|
||||
border-radius: calc(infinity * 1px);
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
--media-tooltip-side-offset: 0.5rem;
|
||||
--media-tooltip-side-offset: 0.75rem;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/* ==========================================================================
|
||||
Media preview
|
||||
========================================================================== */
|
||||
.media-default-skin .media-preview {
|
||||
background-color: oklch(0 0 0 / 0.9);
|
||||
border-radius: 0.75rem;
|
||||
|
||||
& .media-preview__thumbnail {
|
||||
display: block;
|
||||
position: relative;
|
||||
border-radius: inherit;
|
||||
overflow: clip;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background-image: linear-gradient(to top, oklch(0 0 0 / 0.8), oklch(0 0 0 / 0.3), oklch(0 0 0 / 0));
|
||||
}
|
||||
}
|
||||
|
||||
& .media-preview__timestamp {
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
inset-inline: 0;
|
||||
text-align: center;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
& .media-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
& .media-preview__spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
& .media-preview__thumbnail,
|
||||
& .media-preview__spinner {
|
||||
transition: opacity 150ms ease-out;
|
||||
}
|
||||
|
||||
&:has(.media-preview__thumbnail[data-loading]) {
|
||||
& .media-preview__thumbnail {
|
||||
opacity: 0;
|
||||
}
|
||||
& .media-preview__spinner {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
.media-default-skin *::before,
|
||||
.media-default-skin *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
.media-default-skin img,
|
||||
.media-default-skin video,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
flex: 1;
|
||||
border-radius: calc(infinity * 1px);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
|
||||
&[data-orientation="horizontal"] {
|
||||
min-width: 5rem;
|
||||
@@ -141,8 +142,3 @@
|
||||
height: var(--media-slider-fill);
|
||||
}
|
||||
}
|
||||
|
||||
/* Time display within slider */
|
||||
.media-default-skin .media-slider__time-display {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
background-color: var(--media-surface-background-color);
|
||||
backdrop-filter: var(--media-surface-backdrop-filter);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px var(--media-surface-inner-border-color),
|
||||
0 0 0 1px var(--media-surface-outer-border-color),
|
||||
0 1px 3px 0 var(--media-surface-shadow-color),
|
||||
0 1px 2px -1px var(--media-surface-shadow-color);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
inset: 0;
|
||||
z-index: 10;
|
||||
border-radius: inherit;
|
||||
box-shadow: 0 0 0 1px var(--media-surface-outer-border-color);
|
||||
box-shadow: inset 0 0 0 1px var(--media-surface-inner-border-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
@import "./components/time.css";
|
||||
@import "./components/buttons.css";
|
||||
@import "./components/icons.css";
|
||||
@import "./components/preview.css";
|
||||
@import "./components/slider.css";
|
||||
@import "./components/popup.css";
|
||||
@import "./components/captions.css";
|
||||
@@ -99,3 +100,29 @@
|
||||
background-color: oklch(1 0 0 / 0.2);
|
||||
box-shadow: 0 0 0 1px oklch(0 0 0 / 0.05);
|
||||
}
|
||||
|
||||
.media-default-skin .media-slider__preview {
|
||||
position: absolute;
|
||||
left: var(--media-slider-pointer);
|
||||
bottom: calc(100% + 1.2rem);
|
||||
translate: -50%;
|
||||
opacity: 0;
|
||||
scale: 0.8;
|
||||
filter: blur(8px);
|
||||
transition-property: scale, opacity, filter;
|
||||
transition-duration: 150ms;
|
||||
transform-origin: bottom;
|
||||
|
||||
& .media-preview__thumbnail {
|
||||
max-width: 11rem;
|
||||
}
|
||||
|
||||
&:has(.media-preview__thumbnail[data-loading]) {
|
||||
max-height: 6rem;
|
||||
}
|
||||
}
|
||||
.media-default-skin .media-slider[data-pointing] .media-slider__preview:has([role="img"]:not([data-hidden])) {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cn } from '@videojs/utils/style';
|
||||
|
||||
export const button = {
|
||||
base: cn(
|
||||
'items-center justify-center shrink-0 border-none cursor-pointer select-none text-center',
|
||||
'items-center justify-center shrink-0 border-none cursor-pointer select-none text-center touch-manipulation',
|
||||
'font-medium',
|
||||
'outline-2 outline-transparent -outline-offset-2',
|
||||
'transition-[background-color,color,outline-offset,scale] duration-150 ease-out',
|
||||
|
||||
@@ -4,9 +4,9 @@ const base = cn(
|
||||
// Reset default popover styles
|
||||
'm-0 border-0 text-inherit overflow-visible',
|
||||
// Animation
|
||||
'transition-[transform,scale,opacity,filter] duration-200',
|
||||
'data-starting-style:opacity-0 data-starting-style:scale-0 data-starting-style:blur-sm',
|
||||
'data-ending-style:opacity-0 data-ending-style:scale-0 data-ending-style:blur-sm',
|
||||
'transition-[transform,scale,opacity,filter] duration-150',
|
||||
'data-starting-style:opacity-0 data-starting-style:scale-50 data-starting-style:blur-sm',
|
||||
'data-ending-style:opacity-0 data-ending-style:scale-50 data-ending-style:blur-sm',
|
||||
'data-instant:duration-0',
|
||||
// Ensure we animate from the correct origin based on the side the popover is on
|
||||
'data-[side=top]:origin-bottom data-[side=bottom]:origin-top data-[side=left]:origin-right data-[side=right]:origin-left'
|
||||
@@ -17,7 +17,7 @@ export const popup = {
|
||||
tooltip: cn(
|
||||
base,
|
||||
'py-1 px-2.5 rounded-full text-[0.75rem] whitespace-nowrap',
|
||||
'[--media-tooltip-side-offset:0.5rem]'
|
||||
'[--media-tooltip-side-offset:0.75rem]'
|
||||
),
|
||||
volume: 'py-2.5 px-1 rounded-full',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { cn } from '@videojs/utils/style';
|
||||
|
||||
export const preview = {
|
||||
root: cn('group/preview pointer-events-none bg-black/90 rounded-xl'),
|
||||
thumbnail: cn(
|
||||
'block relative overflow-clip rounded-[inherit]',
|
||||
'transition-opacity duration-150 ease-out',
|
||||
'after:absolute after:inset-0 after:rounded-[inherit]',
|
||||
'after:bg-linear-to-t after:from-black/80 after:via-black/30 after:to-black/0',
|
||||
'data-loading:opacity-0'
|
||||
),
|
||||
timestamp: 'absolute bottom-2 inset-x-0 text-center tabular-nums',
|
||||
spinner: cn(
|
||||
'absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0',
|
||||
'transition-opacity duration-150 ease-out',
|
||||
'group-has-[[role=img][data-loading]]/preview:opacity-100'
|
||||
),
|
||||
};
|
||||
@@ -7,7 +7,7 @@ export const root = cn(
|
||||
'rounded-(--media-border-radius,2rem)',
|
||||
'font-[Inter_Variable,Inter,ui-sans-serif,system-ui,sans-serif] text-[0.8125rem] leading-normal subpixel-antialiased',
|
||||
// Resets
|
||||
'**:box-border **:m-0',
|
||||
'**:box-border',
|
||||
'[&_button]:font-[inherit]',
|
||||
'motion-safe:[interpolate-size:allow-keywords]'
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cn } from '@videojs/utils/style';
|
||||
|
||||
export const slider = {
|
||||
root: cn(
|
||||
'group/slider relative flex flex-1 items-center justify-center rounded-full outline-none',
|
||||
'group/slider relative flex flex-1 items-center justify-center rounded-full outline-none cursor-pointer',
|
||||
// Horizontal
|
||||
'data-[orientation=horizontal]:min-w-20 data-[orientation=horizontal]:w-full data-[orientation=horizontal]:h-5',
|
||||
// Vertical
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cn } from '@videojs/utils/style';
|
||||
|
||||
export const surface = cn(
|
||||
// Border and shadow
|
||||
'ring ring-inset shadow-sm',
|
||||
'ring shadow-sm',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'after:absolute after:inset-0 after:ring after:rounded-[inherit] after:pointer-events-none after:z-10'
|
||||
'after:absolute after:inset-0 after:ring after:ring-inset after:rounded-[inherit] after:pointer-events-none after:z-10'
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { bufferingIndicator as baseBufferingIndicator } from './components/buffe
|
||||
import { controls as baseControls } from './components/controls';
|
||||
import { error as baseError } from './components/error';
|
||||
import { popup as basePopup } from './components/popup';
|
||||
import { preview as basePreview } from './components/preview';
|
||||
import { root as baseRoot } from './components/root';
|
||||
import { slider as baseSlider } from './components/slider';
|
||||
import { surface as baseSurface } from './components/surface';
|
||||
@@ -66,9 +67,9 @@ export const surface = cn(
|
||||
'bg-white/10',
|
||||
'backdrop-saturate-150 backdrop-blur-lg',
|
||||
// Border and shadow
|
||||
'ring-white/5 shadow-black/10',
|
||||
'ring-black/15 shadow-black/10',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'after:ring-black/15',
|
||||
'after:ring-white/5',
|
||||
// Reduced transparency for users with preference
|
||||
'[@media(prefers-reduced-transparency:reduce)]:bg-black/70',
|
||||
// High contrast mode
|
||||
@@ -101,6 +102,25 @@ export const controls = cn(
|
||||
'motion-reduce:not-data-visible:scale-100'
|
||||
);
|
||||
|
||||
/* ==========================================================================
|
||||
Preview (with video surface)
|
||||
========================================================================== */
|
||||
|
||||
export const preview = {
|
||||
...basePreview,
|
||||
root: cn(
|
||||
'absolute left-(--media-slider-pointer) bottom-[calc(100%+1.2rem)] -translate-x-1/2',
|
||||
'opacity-0 scale-80 blur-sm origin-bottom',
|
||||
'transition-[scale,opacity,filter] duration-150',
|
||||
'group-data-pointing/slider:opacity-100 group-data-pointing/slider:scale-100 group-data-pointing/slider:blur-none',
|
||||
'[&:has([role=img][data-hidden])]:opacity-0 [&:has([role=img][data-hidden])]:scale-80 [&:has([role=img][data-hidden])]:blur-sm',
|
||||
'[&:has([role=img][data-loading])]:max-h-24',
|
||||
surface,
|
||||
basePreview.root
|
||||
),
|
||||
thumbnail: cn(basePreview.thumbnail, 'max-w-44'),
|
||||
};
|
||||
|
||||
/* ==========================================================================
|
||||
Sliders
|
||||
========================================================================== */
|
||||
|
||||
Reference in New Issue
Block a user