mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(packages): add poster component to video skins (#994)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import type { CSSProperties, PropsWithChildren } from 'react';
|
||||
import type { Poster } from '@/ui/poster';
|
||||
import type { RenderProp } from '@/utils/types';
|
||||
|
||||
export type BaseSkinProps<T = unknown> = PropsWithChildren<
|
||||
T & {
|
||||
@@ -6,3 +8,7 @@ export type BaseSkinProps<T = unknown> = PropsWithChildren<
|
||||
className?: string;
|
||||
}
|
||||
>;
|
||||
|
||||
export type BaseVideoSkinProps<T = unknown> = BaseSkinProps<T> & {
|
||||
poster?: string | RenderProp<Poster.State> | undefined;
|
||||
};
|
||||
|
||||
@@ -26,12 +26,14 @@ import {
|
||||
iconState,
|
||||
overlay,
|
||||
popup,
|
||||
poster,
|
||||
preview,
|
||||
root,
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/minimal/tailwind/video.tailwind';
|
||||
import { isString } from '@videojs/utils/predicate';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container, usePlayer } from '@/player/context';
|
||||
@@ -44,12 +46,14 @@ import { PiPButton } from '@/ui/pip-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import { isRenderProp } from '@/utils/use-render';
|
||||
import { ErrorDialog } from './error-dialog';
|
||||
import type { MinimalVideoSkinProps } from './minimal-skin';
|
||||
|
||||
@@ -142,12 +146,20 @@ function FullscreenLabel(): ReactNode {
|
||||
/* ------------------------------------------ Skin ------------------------------------------- */
|
||||
|
||||
export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
const { children, className, poster: posterProp, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root(false), className)} {...rest}>
|
||||
{children}
|
||||
|
||||
{posterProp && (
|
||||
<Poster
|
||||
src={isString(posterProp) ? posterProp : undefined}
|
||||
render={isRenderProp(posterProp) ? posterProp : undefined}
|
||||
className={poster(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className={bufferingIndicator}>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react/minimal';
|
||||
import { isString } from '@videojs/utils/predicate';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container, usePlayer } from '@/player/context';
|
||||
@@ -25,18 +26,20 @@ import { PiPButton } from '@/ui/pip-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { BaseSkinProps } from '../types';
|
||||
import { isRenderProp } from '@/utils/use-render';
|
||||
import type { BaseVideoSkinProps } from '../types';
|
||||
import { ErrorDialog } from './error-dialog';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
export type MinimalVideoSkinProps = BaseSkinProps;
|
||||
export type MinimalVideoSkinProps = BaseVideoSkinProps;
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {
|
||||
return <button ref={ref} type="button" className={cn('media-button', className)} {...props} />;
|
||||
@@ -75,12 +78,16 @@ function FullscreenLabel(): ReactNode {
|
||||
}
|
||||
|
||||
export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
const { children, className, poster, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-minimal-skin media-minimal-skin--video', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
{poster && (
|
||||
<Poster src={isString(poster) ? poster : undefined} render={isRenderProp(poster) ? poster : undefined} />
|
||||
)}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className="media-buffering-indicator">
|
||||
|
||||
@@ -25,12 +25,14 @@ import {
|
||||
overlay,
|
||||
playbackRate,
|
||||
popup,
|
||||
poster,
|
||||
preview,
|
||||
root,
|
||||
seek,
|
||||
slider,
|
||||
time,
|
||||
} from '@videojs/skins/default/tailwind/video.tailwind';
|
||||
import { isString } from '@videojs/utils/predicate';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container, usePlayer } from '@/player/context';
|
||||
@@ -43,12 +45,14 @@ import { PiPButton } from '@/ui/pip-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import { isRenderProp } from '@/utils/use-render';
|
||||
import { ErrorDialog } from './error-dialog';
|
||||
import type { VideoSkinProps } from './skin';
|
||||
|
||||
@@ -142,12 +146,20 @@ function FullscreenLabel(): ReactNode {
|
||||
/* ------------------------------------------ Skin ------------------------------------------- */
|
||||
|
||||
export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
const { children, className, poster: posterProp, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn(root(false), className)} {...rest}>
|
||||
{children}
|
||||
|
||||
{posterProp && (
|
||||
<Poster
|
||||
src={isString(posterProp) ? posterProp : undefined}
|
||||
render={isRenderProp(posterProp) ? posterProp : undefined}
|
||||
className={poster(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className={bufferingIndicator.root}>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
VolumeLowIcon,
|
||||
VolumeOffIcon,
|
||||
} from '@videojs/icons/react';
|
||||
import { isString } from '@videojs/utils/predicate';
|
||||
import { cn } from '@videojs/utils/style';
|
||||
import { type ComponentProps, forwardRef, type ReactNode } from 'react';
|
||||
import { Container, usePlayer } from '@/player/context';
|
||||
@@ -25,18 +26,20 @@ import { PiPButton } from '@/ui/pip-button';
|
||||
import { PlayButton } from '@/ui/play-button';
|
||||
import { PlaybackRateButton } from '@/ui/playback-rate-button';
|
||||
import { Popover } from '@/ui/popover';
|
||||
import { Poster } from '@/ui/poster';
|
||||
import { SeekButton } from '@/ui/seek-button';
|
||||
import { Slider } from '@/ui/slider';
|
||||
import { Time } from '@/ui/time';
|
||||
import { TimeSlider } from '@/ui/time-slider';
|
||||
import { Tooltip } from '@/ui/tooltip';
|
||||
import { VolumeSlider } from '@/ui/volume-slider';
|
||||
import type { BaseSkinProps } from '../types';
|
||||
import { isRenderProp } from '@/utils/use-render';
|
||||
import type { BaseVideoSkinProps } from '../types';
|
||||
import { ErrorDialog } from './error-dialog';
|
||||
|
||||
const SEEK_TIME = 10;
|
||||
|
||||
export type VideoSkinProps = BaseSkinProps;
|
||||
export type VideoSkinProps = BaseVideoSkinProps;
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ComponentProps<'button'>>(function Button({ className, ...props }, ref) {
|
||||
return <button ref={ref} type="button" className={cn('media-button', className)} {...props} />;
|
||||
@@ -75,12 +78,16 @@ function FullscreenLabel(): ReactNode {
|
||||
}
|
||||
|
||||
export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
const { children, className, ...rest } = props;
|
||||
const { children, className, poster, ...rest } = props;
|
||||
|
||||
return (
|
||||
<Container className={cn('media-default-skin media-default-skin--video', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
{poster && (
|
||||
<Poster src={isString(poster) ? poster : undefined} render={isRenderProp(poster) ? poster : undefined} />
|
||||
)}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props) => (
|
||||
<div {...props} className="media-buffering-indicator">
|
||||
|
||||
@@ -8,6 +8,11 @@ import { mergeProps } from './merge-props';
|
||||
import type { HTMLProps, RenderProp } from './types';
|
||||
import { composeRefs } from './use-composed-refs';
|
||||
|
||||
/** Check if a value is a render prop (function or React element). */
|
||||
export function isRenderProp(value: unknown): value is RenderProp<any> {
|
||||
return isFunction(value) || isValidElement(value);
|
||||
}
|
||||
|
||||
type IntrinsicTagName = keyof React.JSX.IntrinsicElements;
|
||||
|
||||
export interface UseRenderComponentProps<State> {
|
||||
|
||||
Reference in New Issue
Block a user