Files
v10/packages/react/src/player/create-player.tsx
T

106 lines
3.0 KiB
TypeScript

'use client';
import type {
AnyPlayerFeature,
AnyPlayerStore,
AudioFeatures,
AudioPlayerStore,
Media,
PlayerStore,
PlayerTarget,
VideoFeatures,
VideoPlayerStore,
} from '@videojs/core/dom';
import type { InferStoreState } from '@videojs/store';
import { combine, createStore } from '@videojs/store';
import { useStore } from '@videojs/store/react';
import type { FC, ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { useDestroy } from '../utils/use-destroy';
import { Container, PlayerContextProvider, useMedia, usePlayerContext } from './context';
export interface CreatePlayerConfig<Features extends AnyPlayerFeature[]> {
features: Features;
displayName?: string;
}
export interface ProviderProps {
children: ReactNode;
}
export interface CreatePlayerResult<Store extends PlayerStore> {
Provider: FC<ProviderProps>;
Container: typeof Container;
usePlayer: UsePlayerHook<Store>;
useMedia: () => Media | null;
}
export type UsePlayerHook<Store extends PlayerStore> = {
(): Store;
<R>(selector: (state: InferStoreState<Store>) => R): R;
};
/**
* Create a player instance with typed store, Provider component, Container, and hooks.
*
* @label Video
* @param config - Player configuration with features and optional display name.
*/
export function createPlayer(config: CreatePlayerConfig<VideoFeatures>): CreatePlayerResult<VideoPlayerStore>;
/**
* Create a player for audio media.
*
* @label Audio
* @param config - Player configuration with features and optional display name.
*/
export function createPlayer(config: CreatePlayerConfig<AudioFeatures>): CreatePlayerResult<AudioPlayerStore>;
/**
* Create a player with custom features.
*
* @label Generic
* @param config - Player configuration with features and optional display name.
*/
export function createPlayer<const Features extends AnyPlayerFeature[]>(
config: CreatePlayerConfig<Features>
): CreatePlayerResult<PlayerStore<Features>>;
export function createPlayer(config: CreatePlayerConfig<AnyPlayerFeature[]>): CreatePlayerResult<AnyPlayerStore> {
function Provider({ children }: ProviderProps): ReactNode {
const [store] = useState(() => createStore<PlayerTarget>()(combine(...config.features)));
const [media, setMedia] = useState<Media | null>(null);
const [container, setContainer] = useState<HTMLElement | null>(null);
useDestroy(store);
useEffect(() => {
if (!media) return;
return store.attach({ media, container });
}, [media, container, store]);
return (
<PlayerContextProvider value={{ store, media, setMedia, container, setContainer }}>
{children}
</PlayerContextProvider>
);
}
if (__DEV__ && config.displayName) {
Provider.displayName = `${config.displayName}.Provider`;
}
function usePlayer<R>(selector?: (state: object) => R): AnyPlayerStore | R {
const { store } = usePlayerContext();
return useStore(store, selector as any);
}
return {
Provider,
Container,
usePlayer,
useMedia,
};
}