mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(skin): add captions button to video skins (#612)
This commit is contained in:
@@ -103,7 +103,7 @@
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background-image: linear-gradient(to top, oklch(0 0 0 / 0.7), oklch(0 0 0 / 0.5) 7.5rem, oklch(0 0 0 / 0));
|
||||
backdrop-filter: blur(0) saturate(1.5) brightness(0.9);
|
||||
backdrop-filter: blur(0) saturate(1.2) brightness(0.9);
|
||||
opacity: 0;
|
||||
transition-property: opacity, backdrop-filter;
|
||||
transition-duration: 500ms;
|
||||
@@ -123,7 +123,7 @@
|
||||
transition-delay: 0ms;
|
||||
}
|
||||
.media-minimal-skin .media-error[data-visible] ~ .media-overlay {
|
||||
backdrop-filter: blur(8px) saturate(1.5) brightness(0.9);
|
||||
backdrop-filter: blur(8px) saturate(1.2) brightness(0.9);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { FullscreenButtonState, MuteButtonState, PlayButtonState } from '@videojs/core';
|
||||
import {
|
||||
CaptionsOffIcon,
|
||||
CaptionsOnIcon,
|
||||
FullscreenEnterIcon,
|
||||
FullscreenExitIcon,
|
||||
PauseIcon,
|
||||
@@ -16,6 +18,7 @@ import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { BufferingIndicator } from '@/ui/buffering-indicator';
|
||||
import { CaptionsButton, type CaptionsButtonState } from '@/ui/captions-button';
|
||||
import { Controls } from '@/ui/controls';
|
||||
import { ErrorDialog } from '@/ui/error-dialog';
|
||||
import { FullscreenButton } from '@/ui/fullscreen-button';
|
||||
@@ -98,6 +101,16 @@ function MuteButtonIcon({ state, className, ...rest }: { state: MuteButtonState
|
||||
);
|
||||
}
|
||||
|
||||
function CaptionsButtonIcon({ state, className, ...rest }: { state: CaptionsButtonState } & ComponentProps<'svg'>) {
|
||||
const { active } = state;
|
||||
return (
|
||||
<>
|
||||
<CaptionsOffIcon {...rest} className={cn(className, { [iconHidden]: active })} />
|
||||
<CaptionsOnIcon {...rest} className={cn(className, { [iconHidden]: !active })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FullscreenButtonIcon({ state, className, ...rest }: { state: FullscreenButtonState } & ComponentProps<'svg'>) {
|
||||
const { fullscreen } = state;
|
||||
return (
|
||||
@@ -278,6 +291,14 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
)}
|
||||
/>
|
||||
|
||||
<CaptionsButton
|
||||
render={(props, state) => (
|
||||
<Button variant="icon" {...props}>
|
||||
<CaptionsButtonIcon state={state} className={icon} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<PiPButton
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props}>
|
||||
@@ -330,7 +351,7 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
// Default: hidden
|
||||
'opacity-0',
|
||||
'bg-gradient-to-t from-black/70 via-black/50 via-[7.5rem] to-transparent',
|
||||
'backdrop-blur-[0px] backdrop-saturate-150 backdrop-brightness-90',
|
||||
'backdrop-blur-[0px] backdrop-saturate-120 backdrop-brightness-90',
|
||||
// Transitions
|
||||
'transition-[opacity,backdrop-filter] ease-out',
|
||||
'duration-500 delay-500',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { FullscreenButtonState, MuteButtonState, PlayButtonState } from '@videojs/core';
|
||||
import {
|
||||
CaptionsOffIcon,
|
||||
CaptionsOnIcon,
|
||||
FullscreenEnterIcon,
|
||||
FullscreenExitIcon,
|
||||
PauseIcon,
|
||||
@@ -16,6 +18,7 @@ import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { BufferingIndicator } from '@/ui/buffering-indicator';
|
||||
import { CaptionsButton, type CaptionsButtonState } from '@/ui/captions-button';
|
||||
import { Controls } from '@/ui/controls';
|
||||
import { ErrorDialog } from '@/ui/error-dialog';
|
||||
import { FullscreenButton } from '@/ui/fullscreen-button';
|
||||
@@ -56,6 +59,16 @@ function MuteButtonIcon({ state, className, ...rest }: { state: MuteButtonState
|
||||
);
|
||||
}
|
||||
|
||||
function CaptionsButtonIcon({ state, className, ...rest }: { state: CaptionsButtonState } & ComponentProps<'svg'>) {
|
||||
const { active } = state;
|
||||
return (
|
||||
<>
|
||||
<CaptionsOffIcon {...rest} className={cn(className, { 'media-icon--hidden': active })} />
|
||||
<CaptionsOnIcon {...rest} className={cn(className, { 'media-icon--hidden': !active })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FullscreenButtonIcon({ state, className, ...rest }: { state: FullscreenButtonState } & ComponentProps<'svg'>) {
|
||||
const { fullscreen } = state;
|
||||
return (
|
||||
@@ -160,6 +173,14 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
)}
|
||||
/>
|
||||
|
||||
<CaptionsButton
|
||||
render={(props, state) => (
|
||||
<Button {...props} className="media-button--icon">
|
||||
<CaptionsButtonIcon state={state} className="media-icon" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<PiPButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon">
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background-image: linear-gradient(to top, oklch(0 0 0 / 0.5), oklch(0 0 0 / 0.3), oklch(0 0 0 / 0));
|
||||
backdrop-filter: blur(0) saturate(1.5) brightness(0.9);
|
||||
backdrop-filter: blur(0) saturate(1.2) brightness(0.9);
|
||||
opacity: 0;
|
||||
transition-property: opacity, backdrop-filter;
|
||||
transition-duration: 300ms;
|
||||
@@ -126,7 +126,7 @@
|
||||
transition-delay: 0ms;
|
||||
}
|
||||
.media-default-skin .media-error[data-visible] ~ .media-overlay {
|
||||
backdrop-filter: blur(8px) saturate(1.5) brightness(0.9);
|
||||
backdrop-filter: blur(8px) saturate(1.2) brightness(0.9);
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { FullscreenButtonState, MuteButtonState, PlayButtonState } from '@videojs/core';
|
||||
import {
|
||||
CaptionsOffIcon,
|
||||
CaptionsOnIcon,
|
||||
FullscreenEnterIcon,
|
||||
FullscreenExitIcon,
|
||||
PauseIcon,
|
||||
@@ -16,6 +18,7 @@ import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { BufferingIndicator } from '@/ui/buffering-indicator';
|
||||
import { CaptionsButton, type CaptionsButtonState } from '@/ui/captions-button';
|
||||
import { Controls } from '@/ui/controls';
|
||||
import { ErrorDialog } from '@/ui/error-dialog';
|
||||
import { FullscreenButton } from '@/ui/fullscreen-button';
|
||||
@@ -112,6 +115,16 @@ function MuteButtonIcon({ state, className, ...rest }: { state: MuteButtonState
|
||||
);
|
||||
}
|
||||
|
||||
function CaptionsButtonIcon({ state, className, ...rest }: { state: CaptionsButtonState } & ComponentProps<'svg'>) {
|
||||
const { active } = state;
|
||||
return (
|
||||
<>
|
||||
<CaptionsOffIcon {...rest} className={cn(className, { [iconHidden]: active })} />
|
||||
<CaptionsOnIcon {...rest} className={cn(className, { [iconHidden]: !active })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FullscreenButtonIcon({ state, className, ...rest }: { state: FullscreenButtonState } & ComponentProps<'svg'>) {
|
||||
const { fullscreen } = state;
|
||||
return (
|
||||
@@ -277,6 +290,14 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Time.Value type="duration" className="text-shadow-2xs text-shadow-black/25 tabular-nums" />
|
||||
</Time.Group>
|
||||
|
||||
<CaptionsButton
|
||||
render={(props, state) => (
|
||||
<Button variant="icon" {...props}>
|
||||
<CaptionsButtonIcon state={state} className={icon} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<MuteButton
|
||||
render={(props, state) => (
|
||||
<Button variant="icon" {...props}>
|
||||
@@ -336,7 +357,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
// Default: hidden
|
||||
'opacity-0',
|
||||
'bg-gradient-to-t from-black/50 via-black/30 to-transparent',
|
||||
'backdrop-blur-[0px] backdrop-saturate-150 backdrop-brightness-90',
|
||||
'backdrop-blur-[0px] backdrop-saturate-120 backdrop-brightness-90',
|
||||
// Transitions
|
||||
'transition-[opacity,backdrop-filter] ease-out',
|
||||
'duration-300 delay-500',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { FullscreenButtonState, MuteButtonState, PlayButtonState } from '@videojs/core';
|
||||
import {
|
||||
CaptionsOffIcon,
|
||||
CaptionsOnIcon,
|
||||
FullscreenEnterIcon,
|
||||
FullscreenExitIcon,
|
||||
PauseIcon,
|
||||
@@ -16,6 +18,7 @@ import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { BufferingIndicator } from '@/ui/buffering-indicator';
|
||||
import { CaptionsButton, type CaptionsButtonState } from '@/ui/captions-button';
|
||||
import { Controls } from '@/ui/controls';
|
||||
import { ErrorDialog } from '@/ui/error-dialog';
|
||||
import { FullscreenButton } from '@/ui/fullscreen-button';
|
||||
@@ -56,6 +59,16 @@ function MuteButtonIcon({ state, className, ...rest }: { state: MuteButtonState
|
||||
);
|
||||
}
|
||||
|
||||
function CaptionsButtonIcon({ state, className, ...rest }: { state: CaptionsButtonState } & ComponentProps<'svg'>) {
|
||||
const { active } = state;
|
||||
return (
|
||||
<>
|
||||
<CaptionsOffIcon {...rest} className={cn(className, { 'media-icon--hidden': active })} />
|
||||
<CaptionsOnIcon {...rest} className={cn(className, { 'media-icon--hidden': !active })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function FullscreenButtonIcon({ state, className, ...rest }: { state: FullscreenButtonState } & ComponentProps<'svg'>) {
|
||||
const { fullscreen } = state;
|
||||
return (
|
||||
@@ -155,6 +168,14 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
)}
|
||||
/>
|
||||
|
||||
<CaptionsButton
|
||||
render={(props, state) => (
|
||||
<Button {...props} className="media-button--icon">
|
||||
<CaptionsButtonIcon state={state} className="media-icon" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<PiPButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon">
|
||||
|
||||
Reference in New Issue
Block a user