mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(skin): add audio skins for HTML and React presets (#772)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
038ad8b4f5
commit
d751fdabea
@@ -1,4 +1,6 @@
|
||||
export { audioFeatures } from '@videojs/core/dom';
|
||||
export { Audio, type AudioProps } from '@/media/audio';
|
||||
export * from './minimal-skin';
|
||||
export * from './minimal-skin.tailwind';
|
||||
export * from './skin';
|
||||
export * from './skin.tailwind';
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
.media-minimal-skin {
|
||||
color: var(--media-color, red);
|
||||
}
|
||||
@import "@videojs/skins/minimal/css/audio.css";
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react/minimal';
|
||||
import { playbackRate } from '@videojs/skins/default/tailwind/audio.tailwind';
|
||||
import {
|
||||
button,
|
||||
buttonGroup,
|
||||
controls,
|
||||
icon,
|
||||
iconContainer,
|
||||
iconFlipped,
|
||||
iconState,
|
||||
popup,
|
||||
root,
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/minimal/tailwind/audio.tailwind';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { MuteButton } from '@/ui/mute-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { MinimalAudioSkinProps } from './minimal-skin';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
/* --------------------------------------- Components ---------------------------------------- */
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'> & { variant?: 'icon' }>(function Button(
|
||||
{ className, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
className={cn(button.base, variant === 'icon' ? button.icon : button.default, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const SliderRoot = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderRoot({ className, ...props }, ref) {
|
||||
return <div ref={ref} className={cn(slider.root, className)} {...props} />;
|
||||
});
|
||||
|
||||
const SliderTrack = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderTrack(
|
||||
{ className, ...props },
|
||||
ref
|
||||
) {
|
||||
return <div ref={ref} className={cn(slider.track, className)} {...props} />;
|
||||
});
|
||||
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'> & { type?: 'fill' | 'buffer' }>(function SliderFill(
|
||||
{ type = 'fill', className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(slider.fill.base, type === 'fill' ? slider.fill.fill : slider.fill.buffer, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const SliderThumb = forwardRef<HTMLDivElement, ComponentProps<'div'> & { persistent?: boolean }>(function SliderThumb(
|
||||
{ persistent, className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(slider.thumb.base, persistent ? undefined : slider.thumb.interactive, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
/* ------------------------------------------ Skin ------------------------------------------- */
|
||||
|
||||
export function MinimalAudioSkinTailwind(props: MinimalAudioSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root, className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<div className={controls}>
|
||||
<span className={buttonGroup}>
|
||||
<PlayButton
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={iconState.play.button}>
|
||||
<RestartIcon className={cn(icon, iconState.play.restart)} />
|
||||
<PlayIcon className={cn(icon, iconState.play.play)} />
|
||||
<PauseIcon className={cn(icon, iconState.play.pause)} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={-SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={seek.button}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={cn(icon, iconFlipped)} />
|
||||
<span className={cn(seek.label, seek.labelBackward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={seek.button}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={icon} />
|
||||
<span className={cn(seek.label, seek.labelForward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span className={time.controls}>
|
||||
<Time.Group className={time.group}>
|
||||
<Time.Value type="current" className={time.current} />
|
||||
<Time.Separator className={time.separator} />
|
||||
<Time.Value type="duration" className={time.duration} />
|
||||
</Time.Group>
|
||||
|
||||
<TimeSlider.Root render={(props) => <SliderRoot {...props} />}>
|
||||
<TimeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<TimeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
<TimeSlider.Buffer render={(props) => <SliderFill type="buffer" {...props} />} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb render={(props) => <SliderThumb {...props} />} />
|
||||
</TimeSlider.Root>
|
||||
</span>
|
||||
|
||||
<span className={buttonGroup}>
|
||||
<PlaybackRateButton
|
||||
render={(props) => <Button variant="icon" {...props} className={playbackRate.button} />}
|
||||
/>
|
||||
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="left">
|
||||
<Popover.Trigger
|
||||
render={
|
||||
<MuteButton
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={iconState.mute.button}>
|
||||
<VolumeOffIcon className={cn(icon, iconState.mute.volumeOff)} />
|
||||
<VolumeLowIcon className={cn(icon, iconState.mute.volumeLow)} />
|
||||
<VolumeHighIcon className={cn(icon, iconState.mute.volumeHigh)} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Popover.Popup className={cn(popup.base, popup.volume)}>
|
||||
<VolumeSlider.Root
|
||||
orientation="horizontal"
|
||||
thumbAlignment="edge"
|
||||
render={(props) => <SliderRoot {...props} />}
|
||||
>
|
||||
<VolumeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<VolumeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
</VolumeSlider.Track>
|
||||
<VolumeSlider.Thumb render={(props) => <SliderThumb persistent {...props} />} />
|
||||
</VolumeSlider.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Root>
|
||||
</span>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,123 @@
|
||||
import {
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react/minimal';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { MuteButton } from '@/ui/mute-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { BaseSkinProps } from '../types';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
export type MinimalAudioSkinProps = BaseSkinProps;
|
||||
|
||||
export function MinimalAudioSkin(props: MinimalAudioSkinProps) {
|
||||
const { children } = props;
|
||||
return <div>{children}</div>;
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {
|
||||
return <button ref={ref} type="button" className={cn('media-button', className)} {...props} />;
|
||||
});
|
||||
|
||||
export function MinimalAudioSkin(props: MinimalAudioSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-minimal-skin media-minimal-skin--audio', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<div className="media-controls">
|
||||
<span className="media-button-group">
|
||||
<PlayButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--play">
|
||||
<RestartIcon className="media-icon media-icon--restart" />
|
||||
<PlayIcon className="media-icon media-icon--play" />
|
||||
<PauseIcon className="media-icon media-icon--pause" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={-SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--seek">
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek media-icon--flipped" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--seek">
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span className="media-time-controls">
|
||||
<Time.Group className="media-time">
|
||||
<Time.Value type="current" className="media-time__value media-time__value--current" />
|
||||
<Time.Separator className="media-time__separator" />
|
||||
<Time.Value type="duration" className="media-time__value media-time__value--duration" />
|
||||
</Time.Group>
|
||||
|
||||
<TimeSlider.Root className="media-slider">
|
||||
<TimeSlider.Track className="media-slider__track">
|
||||
<TimeSlider.Fill className="media-slider__fill" />
|
||||
<TimeSlider.Buffer className="media-slider__buffer" />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className="media-slider__thumb" />
|
||||
</TimeSlider.Root>
|
||||
</span>
|
||||
|
||||
<span className="media-button-group">
|
||||
<PlaybackRateButton
|
||||
render={(props) => <Button {...props} className="media-button--icon media-button--playback-rate" />}
|
||||
/>
|
||||
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="left">
|
||||
<Popover.Trigger
|
||||
render={
|
||||
<MuteButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--mute">
|
||||
<VolumeOffIcon className="media-icon media-icon--volume-off" />
|
||||
<VolumeLowIcon className="media-icon media-icon--volume-low" />
|
||||
<VolumeHighIcon className="media-icon media-icon--volume-high" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Popover.Popup className="media-surface media-popup media-popup--volume media-popup-animation">
|
||||
<VolumeSlider.Root className="media-slider" orientation="horizontal" thumbAlignment="edge">
|
||||
<VolumeSlider.Track className="media-slider__track">
|
||||
<VolumeSlider.Fill className="media-slider__fill" />
|
||||
</VolumeSlider.Track>
|
||||
<VolumeSlider.Thumb className="media-slider__thumb media-slider__thumb--persistent" />
|
||||
</VolumeSlider.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Root>
|
||||
</span>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import "@videojs/skins/audio/default.css";
|
||||
@import "@videojs/skins/default/css/audio.css";
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react';
|
||||
import {
|
||||
button,
|
||||
controls,
|
||||
icon,
|
||||
iconContainer,
|
||||
iconFlipped,
|
||||
iconState,
|
||||
playbackRate,
|
||||
popup,
|
||||
root,
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/default/tailwind/audio.tailwind';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { MuteButton } from '@/ui/mute-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { AudioSkinProps } from './skin';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
/* --------------------------------------- Components ---------------------------------------- */
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'> & { variant?: 'icon' }>(function Button(
|
||||
{ className, variant, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type="button"
|
||||
className={cn(button.base, variant === 'icon' ? button.icon : button.default, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const SliderRoot = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderRoot({ className, ...props }, ref) {
|
||||
return <div ref={ref} className={cn(slider.root, className)} {...props} />;
|
||||
});
|
||||
|
||||
const SliderTrack = forwardRef<HTMLDivElement, ComponentProps<'div'>>(function SliderTrack(
|
||||
{ className, ...props },
|
||||
ref
|
||||
) {
|
||||
return <div ref={ref} className={cn(slider.track, className)} {...props} />;
|
||||
});
|
||||
|
||||
const SliderFill = forwardRef<HTMLDivElement, ComponentProps<'div'> & { type?: 'fill' | 'buffer' }>(function SliderFill(
|
||||
{ type = 'fill', className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(slider.fill.base, type === 'fill' ? slider.fill.fill : slider.fill.buffer, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const SliderThumb = forwardRef<HTMLDivElement, ComponentProps<'div'> & { persistent?: boolean }>(function SliderThumb(
|
||||
{ persistent, className, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(slider.thumb.base, persistent ? slider.thumb.persistent : slider.thumb.interactive, className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
/* ------------------------------------------ Skin ------------------------------------------- */
|
||||
|
||||
export function AudioSkinTailwind(props: AudioSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root, className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<div className={controls}>
|
||||
<PlayButton
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={iconState.play.button}>
|
||||
<RestartIcon className={cn(icon, iconState.play.restart)} />
|
||||
<PlayIcon className={cn(icon, iconState.play.play)} />
|
||||
<PauseIcon className={cn(icon, iconState.play.pause)} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={-SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={seek.button}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={cn(icon, iconFlipped)} />
|
||||
<span className={cn(seek.label, seek.labelBackward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={seek.button}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={icon} />
|
||||
<span className={cn(seek.label, seek.labelForward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Time.Group className={time.group}>
|
||||
<Time.Value type="current" className={time.current} />
|
||||
<TimeSlider.Root render={(props) => <SliderRoot {...props} />}>
|
||||
<TimeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<TimeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
<TimeSlider.Buffer render={(props) => <SliderFill type="buffer" {...props} />} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb render={(props) => <SliderThumb {...props} />} />
|
||||
</TimeSlider.Root>
|
||||
<Time.Value type="duration" className={time.duration} />
|
||||
</Time.Group>
|
||||
|
||||
<PlaybackRateButton render={(props) => <Button variant="icon" {...props} className={playbackRate.button} />} />
|
||||
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Trigger
|
||||
render={
|
||||
<MuteButton
|
||||
render={(props) => (
|
||||
<Button variant="icon" {...props} className={iconState.mute.button}>
|
||||
<VolumeOffIcon className={cn(icon, iconState.mute.volumeOff)} />
|
||||
<VolumeLowIcon className={cn(icon, iconState.mute.volumeLow)} />
|
||||
<VolumeHighIcon className={cn(icon, iconState.mute.volumeHigh)} />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Popover.Popup className={cn(popup.base, popup.volume)}>
|
||||
<VolumeSlider.Root
|
||||
orientation="vertical"
|
||||
thumbAlignment="edge"
|
||||
render={(props) => <SliderRoot {...props} />}
|
||||
>
|
||||
<VolumeSlider.Track render={(props) => <SliderTrack {...props} />}>
|
||||
<VolumeSlider.Fill render={(props) => <SliderFill {...props} />} />
|
||||
</VolumeSlider.Track>
|
||||
<VolumeSlider.Thumb render={(props) => <SliderThumb persistent {...props} />} />
|
||||
</VolumeSlider.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Root>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,115 @@
|
||||
import {
|
||||
PauseIcon,
|
||||
PlayIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
VolumeHighIcon,
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
import { MuteButton } from '@/ui/mute-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { BaseSkinProps } from '../types';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
export type AudioSkinProps = BaseSkinProps;
|
||||
|
||||
export function AudioSkin(props: AudioSkinProps) {
|
||||
const { children } = props;
|
||||
return <div>{children}</div>;
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {
|
||||
return <button ref={ref} type="button" className={cn('media-button', className)} {...props} />;
|
||||
});
|
||||
|
||||
export function AudioSkin(props: AudioSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-default-skin media-default-skin--audio', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<div className="media-surface media-controls">
|
||||
<PlayButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--play">
|
||||
<RestartIcon className="media-icon media-icon--restart" />
|
||||
<PlayIcon className="media-icon media-icon--play" />
|
||||
<PauseIcon className="media-icon media-icon--pause" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={-SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--seek">
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek media-icon--flipped" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SeekButton
|
||||
seconds={SEEK_TIME}
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--seek">
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Time.Group className="media-time">
|
||||
<Time.Value type="current" className="media-time__value" />
|
||||
<TimeSlider.Root className="media-slider">
|
||||
<TimeSlider.Track className="media-slider__track">
|
||||
<TimeSlider.Fill className="media-slider__fill" />
|
||||
<TimeSlider.Buffer className="media-slider__buffer" />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className="media-slider__thumb" />
|
||||
</TimeSlider.Root>
|
||||
<Time.Value type="duration" className="media-time__value" />
|
||||
</Time.Group>
|
||||
|
||||
<PlaybackRateButton
|
||||
render={(props) => <Button {...props} className="media-button--icon media-button--playback-rate" />}
|
||||
/>
|
||||
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Trigger
|
||||
render={
|
||||
<MuteButton
|
||||
render={(props) => (
|
||||
<Button {...props} className="media-button--icon media-button--mute">
|
||||
<VolumeOffIcon className="media-icon media-icon--volume-off" />
|
||||
<VolumeLowIcon className="media-icon media-icon--volume-low" />
|
||||
<VolumeHighIcon className="media-icon media-icon--volume-high" />
|
||||
</Button>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Popover.Popup className="media-surface media-popup media-popup--volume media-popup-animation">
|
||||
<VolumeSlider.Root className="media-slider" orientation="vertical" thumbAlignment="edge">
|
||||
<VolumeSlider.Track className="media-slider__track">
|
||||
<VolumeSlider.Fill className="media-slider__fill" />
|
||||
</VolumeSlider.Track>
|
||||
<VolumeSlider.Thumb className="media-slider__thumb media-slider__thumb--persistent" />
|
||||
</VolumeSlider.Root>
|
||||
</Popover.Popup>
|
||||
</Popover.Root>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import "@videojs/skins/background/default.css";
|
||||
@import "@videojs/skins/default/css/background.css";
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import "@videojs/skins/video/minimal.css";
|
||||
@import "@videojs/skins/minimal/css/video.css";
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react/minimal';
|
||||
import { playbackRate } from '@videojs/skins/video/default.tailwind';
|
||||
import { playbackRate } from '@videojs/skins/default/tailwind/video.tailwind';
|
||||
import {
|
||||
bufferingIndicator,
|
||||
button,
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/video/minimal.tailwind';
|
||||
} from '@videojs/skins/minimal/tailwind/video.tailwind';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
@@ -120,7 +120,9 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root, className)} {...rest}>
|
||||
<Container className={cn(root(false), className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className={bufferingIndicator}>
|
||||
@@ -257,8 +259,6 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
</div> */}
|
||||
|
||||
<div className={overlay} />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,9 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-minimal-skin', className)} {...rest}>
|
||||
<Container className={cn('media-minimal-skin media-minimal-skin--video', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className="media-buffering-indicator">
|
||||
@@ -185,8 +187,6 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
</div> */}
|
||||
|
||||
<div className="media-overlay" />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import "@videojs/skins/video/default.css";
|
||||
@import "@videojs/skins/default/css/video.css";
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/video/default.tailwind';
|
||||
} from '@videojs/skins/default/tailwind/video.tailwind';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container } from '@/player/context';
|
||||
@@ -120,7 +120,9 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root, className)} {...rest}>
|
||||
<Container className={cn(root(false), className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className={bufferingIndicator.root}>
|
||||
@@ -249,8 +251,6 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
</div> */}
|
||||
|
||||
<div className={overlay} />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,9 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-default-skin', className)} {...rest}>
|
||||
<Container className={cn('media-default-skin media-default-skin--video', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className="media-buffering-indicator">
|
||||
@@ -179,8 +181,6 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
</div> */}
|
||||
|
||||
<div className="media-overlay" />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user