mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(skin): improved responsive design
This commit is contained in:
@@ -265,14 +265,14 @@ export function MinimalAudioSkinTailwind(props: MinimalAudioSkinProps): ReactNod
|
||||
</div>
|
||||
|
||||
<div className={buttonGroup}>
|
||||
<VolumePopover />
|
||||
|
||||
<Menu.Root side="top" align="center" boundary="viewport">
|
||||
<PlaybackRateTrigger />
|
||||
<Menu.Content className={cn(popup.popover, menu.root)}>
|
||||
<PlaybackRateRadioGroup />
|
||||
</Menu.Content>
|
||||
</Menu.Root>
|
||||
|
||||
<VolumePopover />
|
||||
</div>
|
||||
</Tooltip.Provider>
|
||||
</div>
|
||||
|
||||
@@ -216,14 +216,14 @@ export function MinimalAudioSkin(props: MinimalAudioSkinProps): ReactNode {
|
||||
</div>
|
||||
|
||||
<div className="media-button-group">
|
||||
<VolumePopover />
|
||||
|
||||
<Menu.Root side="top" align="center" boundary="viewport">
|
||||
<PlaybackRateTrigger />
|
||||
<Menu.Content className="media-popover media-menu media-menu--playback-rate">
|
||||
<PlaybackRateRadioGroup />
|
||||
</Menu.Content>
|
||||
</Menu.Root>
|
||||
|
||||
<VolumePopover />
|
||||
</div>
|
||||
</Tooltip.Provider>
|
||||
</div>
|
||||
|
||||
@@ -215,7 +215,7 @@ export function AudioSkinTailwind(props: AudioSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top" boundary="viewport">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={-SEEK_TIME} render={<Button />}>
|
||||
<SeekButton seconds={-SEEK_TIME} render={<Button className="@max-[32rem]/media-root:hidden" />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={cn(icon, iconFlipped)} />
|
||||
<span className={cn(seek.label, seek.labelBackward)}>{SEEK_TIME}</span>
|
||||
@@ -232,7 +232,7 @@ export function AudioSkinTailwind(props: AudioSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top" boundary="viewport">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={SEEK_TIME} render={<Button />}>
|
||||
<SeekButton seconds={SEEK_TIME} render={<Button className="@max-[32rem]/media-root:hidden" />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={icon} />
|
||||
<span className={cn(seek.label, seek.labelForward)}>{SEEK_TIME}</span>
|
||||
|
||||
@@ -126,10 +126,10 @@ function VolumePopover(): ReactNode {
|
||||
if (volumeUnsupported) return muteButton;
|
||||
|
||||
return (
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="right">
|
||||
<Popover.Trigger render={muteButton} />
|
||||
<Popover.Popup className={cn(popup.volume)}>
|
||||
<VolumeSlider.Root orientation="vertical" thumbAlignment="edge" render={<SliderRoot />}>
|
||||
<VolumeSlider.Root orientation="horizontal" thumbAlignment="edge" render={<SliderRoot />}>
|
||||
<VolumeSlider.Track render={<SliderTrack />}>
|
||||
<VolumeSlider.Fill render={<SliderFill />} />
|
||||
</VolumeSlider.Track>
|
||||
@@ -262,13 +262,13 @@ export function MinimalLiveVideoSkinTailwind(props: MinimalLiveVideoSkinProps):
|
||||
</Tooltip.Root>
|
||||
|
||||
<LiveButton className={cn(button.base, button.subtle, button.live)} />
|
||||
|
||||
<VolumePopover />
|
||||
</div>
|
||||
|
||||
<div className="grow" aria-hidden="true" />
|
||||
|
||||
<div className={buttonGroupEnd}>
|
||||
<VolumePopover />
|
||||
|
||||
<CaptionsTrigger />
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
|
||||
@@ -79,10 +79,10 @@ function VolumePopover(): ReactNode {
|
||||
if (volumeUnsupported) return muteButton;
|
||||
|
||||
return (
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="right">
|
||||
<Popover.Trigger render={muteButton} />
|
||||
<Popover.Popup className="media-popover media-popover--volume">
|
||||
<VolumeSlider.Root className="media-slider" orientation="vertical" thumbAlignment="edge">
|
||||
<VolumeSlider.Root className="media-slider" orientation="horizontal" thumbAlignment="edge">
|
||||
<VolumeSlider.Track className="media-slider__track">
|
||||
<VolumeSlider.Fill className="media-slider__fill" />
|
||||
</VolumeSlider.Track>
|
||||
@@ -228,13 +228,13 @@ export function MinimalLiveVideoSkin(props: MinimalLiveVideoSkinProps): ReactNod
|
||||
</Tooltip.Root>
|
||||
|
||||
<LiveButton className="media-button media-button--subtle media-button--live" />
|
||||
|
||||
<VolumePopover />
|
||||
</div>
|
||||
|
||||
<div className="media-time-controls" aria-hidden="true" />
|
||||
|
||||
<div className="media-button-group">
|
||||
<VolumePopover />
|
||||
|
||||
<CaptionsTrigger />
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
|
||||
@@ -173,7 +173,7 @@ export function LiveVideoSkin(props: LiveVideoSkinProps): ReactNode {
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={cn('media-default-skin media-default-skin--video', className)}
|
||||
className={cn('media-default-skin media-default-skin--video media-default-skin--live', className)}
|
||||
style={containerStyle}
|
||||
{...rest}
|
||||
>
|
||||
@@ -205,7 +205,7 @@ export function LiveVideoSkin(props: LiveVideoSkinProps): ReactNode {
|
||||
</ErrorDialog.Popup>
|
||||
</ErrorDialog.Root>
|
||||
|
||||
<Controls.Root className="media-surface media-controls">
|
||||
<Controls.Root className="media-surface media-controls media-controls--primary">
|
||||
<Tooltip.Provider>
|
||||
<div className="media-button-group">
|
||||
<Tooltip.Root side="top">
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
controls,
|
||||
error,
|
||||
icon,
|
||||
iconContainer,
|
||||
iconFlipped,
|
||||
iconState,
|
||||
inputFeedback,
|
||||
@@ -16,7 +15,6 @@ import {
|
||||
popup,
|
||||
poster,
|
||||
root,
|
||||
seek,
|
||||
slider,
|
||||
thumbnail,
|
||||
time,
|
||||
@@ -43,7 +41,6 @@ import {
|
||||
PlayIcon,
|
||||
QualityIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
SpeechIcon,
|
||||
SpeedIcon,
|
||||
SpinnerIcon,
|
||||
@@ -71,7 +68,6 @@ import { usePlaybackRateOptions } from '@/ui/playback-rate';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { useQualityOptions } from '@/ui/quality';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { SeekIndicator } from '@/ui/seek-indicator';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { StatusAnnouncer } from '@/ui/status-announcer';
|
||||
@@ -151,10 +147,10 @@ function VolumePopover(): ReactNode {
|
||||
if (volumeUnsupported) return muteButton;
|
||||
|
||||
return (
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="right">
|
||||
<Popover.Trigger render={muteButton} />
|
||||
<Popover.Popup className={cn(popup.volume)}>
|
||||
<VolumeSlider.Root orientation="vertical" thumbAlignment="edge" render={<SliderRoot />}>
|
||||
<Popover.Popup className={popup.volume}>
|
||||
<VolumeSlider.Root orientation="horizontal" thumbAlignment="edge" render={<SliderRoot />}>
|
||||
<VolumeSlider.Track render={<SliderTrack />}>
|
||||
<VolumeSlider.Fill render={<SliderFill />} />
|
||||
</VolumeSlider.Track>
|
||||
@@ -184,11 +180,7 @@ function SettingsMenu(): ReactNode {
|
||||
|
||||
return (
|
||||
<Menu.Root side="top" align="center">
|
||||
<Menu.Trigger
|
||||
aria-label={t('Settings')}
|
||||
className="media-button--settings"
|
||||
render={<Button className={cn(button.icon, menu.settingsTrigger)} />}
|
||||
>
|
||||
<Menu.Trigger aria-label={t('Settings')} render={<Button className={cn(button.icon, menu.settingsTrigger)} />}>
|
||||
<GearIcon className={cn(icon, menu.settingsIcon)} />
|
||||
</Menu.Trigger>
|
||||
<Menu.Content className={menu.settings}>
|
||||
@@ -198,7 +190,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="quality"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<QualityIcon className={cn(icon, menu.icon)} />
|
||||
@@ -252,7 +244,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="audio-track"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<SpeechIcon className={icon} />
|
||||
@@ -302,7 +294,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="playback-rate"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<SpeedIcon className={cn(icon, menu.icon)} />
|
||||
@@ -352,7 +344,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="captions"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<CaptionsOffIcon className={cn(icon, menu.icon)} />
|
||||
@@ -469,39 +461,7 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={-SEEK_TIME} render={<Button />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={cn(icon, iconFlipped)} />
|
||||
<span className={cn(seek.label, seek.labelBackward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</SeekButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={SEEK_TIME} render={<Button />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={icon} />
|
||||
<span className={cn(seek.label, seek.labelForward)}>{SEEK_TIME}</span>
|
||||
</span>
|
||||
</SeekButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
<VolumePopover />
|
||||
</div>
|
||||
|
||||
<div className={time.controls}>
|
||||
@@ -531,8 +491,6 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
</div>
|
||||
|
||||
<div className={cn(buttonGroupEnd, menu.settingsGroup)}>
|
||||
<VolumePopover />
|
||||
|
||||
<SettingsMenu />
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
PlayIcon,
|
||||
QualityIcon,
|
||||
RestartIcon,
|
||||
SeekIcon,
|
||||
SpeechIcon,
|
||||
SpeedIcon,
|
||||
SpinnerIcon,
|
||||
@@ -48,7 +47,6 @@ import { usePlaybackRateOptions } from '@/ui/playback-rate';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { useQualityOptions } from '@/ui/quality';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { SeekIndicator } from '@/ui/seek-indicator';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { StatusAnnouncer } from '@/ui/status-announcer';
|
||||
@@ -92,10 +90,10 @@ function VolumePopover(): ReactNode {
|
||||
if (volumeUnsupported) return muteButton;
|
||||
|
||||
return (
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="top">
|
||||
<Popover.Root openOnHover delay={200} closeDelay={100} side="right">
|
||||
<Popover.Trigger render={muteButton} />
|
||||
<Popover.Popup className="media-popover media-popover--volume">
|
||||
<VolumeSlider.Root className="media-slider" orientation="vertical" thumbAlignment="edge">
|
||||
<VolumeSlider.Root className="media-slider" orientation="horizontal" thumbAlignment="edge">
|
||||
<VolumeSlider.Track className="media-slider__track">
|
||||
<VolumeSlider.Fill className="media-slider__fill" />
|
||||
</VolumeSlider.Track>
|
||||
@@ -401,39 +399,7 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={-SEEK_TIME} className="media-button--seek" render={<Button />}>
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek media-icon--flipped" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</SeekButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={SEEK_TIME} className="media-button--seek" render={<Button />}>
|
||||
<span className="media-icon__container">
|
||||
<SeekIcon className="media-icon media-icon--seek" />
|
||||
<span className="media-icon__label">{SEEK_TIME}</span>
|
||||
</span>
|
||||
</SeekButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
<VolumePopover />
|
||||
</div>
|
||||
|
||||
<div className="media-time-controls">
|
||||
@@ -457,6 +423,7 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
<TimeSlider.Value type="pointer" className="media-time media-thumbnail__time" />
|
||||
<SpinnerIcon className="media-thumbnail__spinner media-icon" />
|
||||
</div>
|
||||
|
||||
<TimeSlider.Preview className="media-slider__preview">
|
||||
<TimeSlider.Value type="pointer" className="media-time media-slider__value" />
|
||||
</TimeSlider.Preview>
|
||||
@@ -464,8 +431,6 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
</div>
|
||||
|
||||
<div className="media-button-group">
|
||||
<VolumePopover />
|
||||
|
||||
<SettingsMenu />
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
popup,
|
||||
poster,
|
||||
root,
|
||||
secondaryControls,
|
||||
seek,
|
||||
slider,
|
||||
thumbnail,
|
||||
@@ -184,11 +185,7 @@ function SettingsMenu(): ReactNode {
|
||||
|
||||
return (
|
||||
<Menu.Root side="top" align="center">
|
||||
<Menu.Trigger
|
||||
aria-label={t('Settings')}
|
||||
className="media-button--settings"
|
||||
render={<Button className={cn(button.icon, menu.settingsTrigger)} />}
|
||||
>
|
||||
<Menu.Trigger aria-label={t('Settings')} render={<Button className={cn(button.icon, menu.settingsTrigger)} />}>
|
||||
<GearIcon className={cn(icon, menu.settingsIcon)} />
|
||||
</Menu.Trigger>
|
||||
<Menu.Content className={menu.settings}>
|
||||
@@ -198,7 +195,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="quality"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<QualityIcon className={cn(icon, menu.icon)} />
|
||||
@@ -252,7 +249,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="audio-track"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<SpeechIcon className={icon} />
|
||||
@@ -302,7 +299,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="playback-rate"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<SpeedIcon className={cn(icon, menu.icon)} />
|
||||
@@ -352,7 +349,7 @@ function SettingsMenu(): ReactNode {
|
||||
<Menu.Root>
|
||||
<Menu.Trigger
|
||||
type="captions"
|
||||
className={cn(menu.item, 'media-menu__item--submenu')}
|
||||
className={menu.item}
|
||||
render={(props) => (
|
||||
<div {...props}>
|
||||
<CaptionsOffIcon className={cn(icon, menu.icon)} />
|
||||
@@ -447,6 +444,72 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
</ErrorDialog.Popup>
|
||||
</ErrorDialog.Root>
|
||||
|
||||
<Controls.Root data-controls="" className={secondaryControls}>
|
||||
<Tooltip.Provider>
|
||||
<div className={buttonGroupEnd}>
|
||||
<Tooltip.Root side="bottom">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<CastButton className={iconState.cast.button} render={<Button />}>
|
||||
<CastEnterIcon className={cn(icon, iconState.cast.enter)} />
|
||||
<CastExitIcon className={cn(icon, iconState.cast.exit)} />
|
||||
</CastButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="bottom">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<AirPlayButton className={iconState.airplay.button} render={<Button />}>
|
||||
<AirPlayEnterIcon className={cn(icon, iconState.airplay.enter)} />
|
||||
<AirPlayExitIcon className={cn(icon, iconState.airplay.exit)} />
|
||||
</AirPlayButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="bottom">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<PiPButton className={iconState.pip.button} render={<Button />}>
|
||||
<PipEnterIcon className={cn(icon, iconState.pip.off)} />
|
||||
<PipExitIcon className={cn(icon, iconState.pip.on)} />
|
||||
</PiPButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="bottom">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<FullscreenButton className={iconState.fullscreen.button} render={<Button />}>
|
||||
<FullscreenEnterIcon className={cn(icon, iconState.fullscreen.enter)} />
|
||||
<FullscreenExitIcon className={cn(icon, iconState.fullscreen.exit)} />
|
||||
</FullscreenButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className={cn(popup.tooltip)}>
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className={popup.tooltipShortcut} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
</Tooltip.Provider>
|
||||
</Controls.Root>
|
||||
|
||||
<Controls.Root
|
||||
data-controls="" // Used as a hook for Tailwind has-[] styles
|
||||
className={controls}
|
||||
@@ -472,7 +535,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={-SEEK_TIME} render={<Button />}>
|
||||
<SeekButton seconds={-SEEK_TIME} render={<Button className="@max-[32rem]/media-root:hidden" />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={cn(icon, iconFlipped)} />
|
||||
<span className={cn(seek.label, seek.labelBackward)}>{SEEK_TIME}</span>
|
||||
@@ -489,7 +552,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<SeekButton seconds={SEEK_TIME} render={<Button />}>
|
||||
<SeekButton seconds={SEEK_TIME} render={<Button className="@max-[32rem]/media-root:hidden" />}>
|
||||
<span className={iconContainer}>
|
||||
<SeekIcon className={icon} />
|
||||
<span className={cn(seek.label, seek.labelForward)}>{SEEK_TIME}</span>
|
||||
@@ -532,7 +595,10 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<CastButton className={iconState.cast.button} render={<Button />}>
|
||||
<CastButton
|
||||
className={cn(iconState.cast.button, '@max-[32rem]/media-root:hidden')}
|
||||
render={<Button />}
|
||||
>
|
||||
<CastEnterIcon className={cn(icon, iconState.cast.enter)} />
|
||||
<CastExitIcon className={cn(icon, iconState.cast.exit)} />
|
||||
</CastButton>
|
||||
@@ -547,7 +613,10 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<AirPlayButton className={iconState.airplay.button} render={<Button />}>
|
||||
<AirPlayButton
|
||||
className={cn(iconState.airplay.button, '@max-[32rem]/media-root:hidden')}
|
||||
render={<Button />}
|
||||
>
|
||||
<AirPlayEnterIcon className={cn(icon, iconState.airplay.enter)} />
|
||||
<AirPlayExitIcon className={cn(icon, iconState.airplay.exit)} />
|
||||
</AirPlayButton>
|
||||
@@ -562,7 +631,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<PiPButton className={iconState.pip.button} render={<Button />}>
|
||||
<PiPButton className={cn(iconState.pip.button, '@max-[32rem]/media-root:hidden')} render={<Button />}>
|
||||
<PipEnterIcon className={cn(icon, iconState.pip.off)} />
|
||||
<PipExitIcon className={cn(icon, iconState.pip.on)} />
|
||||
</PiPButton>
|
||||
@@ -577,7 +646,10 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<FullscreenButton className={iconState.fullscreen.button} render={<Button />}>
|
||||
<FullscreenButton
|
||||
className={cn(iconState.fullscreen.button, '@max-[32rem]/media-root:hidden')}
|
||||
render={<Button />}
|
||||
>
|
||||
<FullscreenEnterIcon className={cn(icon, iconState.fullscreen.enter)} />
|
||||
<FullscreenExitIcon className={cn(icon, iconState.fullscreen.exit)} />
|
||||
</FullscreenButton>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { TooltipProps } from '@videojs/core';
|
||||
import { isString } from '@videojs/utils/predicate';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, type CSSProperties, forwardRef, type ReactNode } from 'react';
|
||||
@@ -341,6 +342,85 @@ function SettingsMenu(): ReactNode {
|
||||
);
|
||||
}
|
||||
|
||||
type ControlProps = {
|
||||
tooltipSide?: TooltipProps['side'];
|
||||
};
|
||||
|
||||
function CastControl({ tooltipSide = 'top' }: ControlProps) {
|
||||
return (
|
||||
<Tooltip.Root side={tooltipSide}>
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<CastButton className="media-button--cast" render={<Button />}>
|
||||
<CastEnterIcon className="media-icon media-icon--cast-enter" />
|
||||
<CastExitIcon className="media-icon media-icon--cast-exit" />
|
||||
</CastButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function AirPlayControl({ tooltipSide = 'top' }: ControlProps) {
|
||||
return (
|
||||
<Tooltip.Root side={tooltipSide}>
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<AirPlayButton className="media-button--airplay" render={<Button />}>
|
||||
<AirPlayEnterIcon className="media-icon media-icon--airplay-enter" />
|
||||
<AirPlayExitIcon className="media-icon media-icon--airplay-exit" />
|
||||
</AirPlayButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function PiPControl({ tooltipSide = 'top' }: ControlProps) {
|
||||
return (
|
||||
<Tooltip.Root side={tooltipSide}>
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<PiPButton className="media-button--pip" render={<Button />}>
|
||||
<PipEnterIcon className="media-icon media-icon--pip-enter" />
|
||||
<PipExitIcon className="media-icon media-icon--pip-exit" />
|
||||
</PiPButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
);
|
||||
}
|
||||
function FullscreenControl({ tooltipSide = 'top' }: ControlProps) {
|
||||
return (
|
||||
<Tooltip.Root side={tooltipSide}>
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<FullscreenButton className="media-button--fullscreen" render={<Button />}>
|
||||
<FullscreenEnterIcon className="media-icon media-icon--fullscreen-enter" />
|
||||
<FullscreenExitIcon className="media-icon media-icon--fullscreen-exit" />
|
||||
</FullscreenButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
const { children, className, poster, placeholder, style, ...rest } = props;
|
||||
|
||||
@@ -382,7 +462,14 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
</ErrorDialog.Popup>
|
||||
</ErrorDialog.Root>
|
||||
|
||||
<Controls.Root className="media-surface media-controls">
|
||||
<Controls.Root className="media-surface media-controls media-controls--secondary">
|
||||
<CastControl tooltipSide="bottom" />
|
||||
<AirPlayControl tooltipSide="bottom" />
|
||||
<PiPControl tooltipSide="bottom" />
|
||||
<FullscreenControl tooltipSide="bottom" />
|
||||
</Controls.Root>
|
||||
|
||||
<Controls.Root className="media-surface media-controls media-controls--primary">
|
||||
<Tooltip.Provider>
|
||||
<div className="media-button-group">
|
||||
<Tooltip.Root side="top">
|
||||
@@ -459,68 +546,11 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
|
||||
<div className="media-button-group">
|
||||
<VolumePopover />
|
||||
|
||||
<SettingsMenu />
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<CastButton className="media-button--cast" render={<Button />}>
|
||||
<CastEnterIcon className="media-icon media-icon--cast-enter" />
|
||||
<CastExitIcon className="media-icon media-icon--cast-exit" />
|
||||
</CastButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<AirPlayButton className="media-button--airplay" render={<Button />}>
|
||||
<AirPlayEnterIcon className="media-icon media-icon--airplay-enter" />
|
||||
<AirPlayExitIcon className="media-icon media-icon--airplay-exit" />
|
||||
</AirPlayButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<PiPButton className="media-button--pip" render={<Button />}>
|
||||
<PipEnterIcon className="media-icon media-icon--pip-enter" />
|
||||
<PipExitIcon className="media-icon media-icon--pip-exit" />
|
||||
</PiPButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
|
||||
<Tooltip.Root side="top">
|
||||
<Tooltip.Trigger
|
||||
render={
|
||||
<FullscreenButton className="media-button--fullscreen" render={<Button />}>
|
||||
<FullscreenEnterIcon className="media-icon media-icon--fullscreen-enter" />
|
||||
<FullscreenExitIcon className="media-icon media-icon--fullscreen-exit" />
|
||||
</FullscreenButton>
|
||||
}
|
||||
/>
|
||||
<Tooltip.Popup className="media-surface media-tooltip">
|
||||
<Tooltip.Label />
|
||||
<Tooltip.Shortcut className="media-tooltip__kbd" />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Root>
|
||||
<CastControl />
|
||||
<AirPlayControl />
|
||||
<PiPControl />
|
||||
<FullscreenControl />
|
||||
</div>
|
||||
</Tooltip.Provider>
|
||||
</Controls.Root>
|
||||
|
||||
Reference in New Issue
Block a user