mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(html): separate provider and container concerns in createPlayer (#635)
This commit is contained in:
@@ -11,11 +11,8 @@ import { combine, createStore } from '@videojs/store';
|
||||
|
||||
import { type ContainerMixin, createContainerMixin } from '../store/container-mixin';
|
||||
import { createProviderMixin, type ProviderMixin } from '../store/provider-mixin';
|
||||
import type { PlayerElementConstructor } from '../store/types';
|
||||
import { MediaElement } from '../ui/media-element';
|
||||
import { type PlayerContext, playerContext } from './context';
|
||||
import { PlayerController } from './player-controller';
|
||||
import { createPlayerMixin, type PlayerMixin } from './player-mixin';
|
||||
|
||||
export interface CreatePlayerConfig<Features extends AnyPlayerFeature[]> {
|
||||
features: Features;
|
||||
@@ -31,12 +28,6 @@ export interface CreatePlayerResult<Store extends PlayerStore> {
|
||||
/** Player controller bound to this player's context. */
|
||||
PlayerController: PlayerController.Constructor<Store>;
|
||||
|
||||
/** Pre-composed player element ready for customElements.define(). */
|
||||
PlayerElement: PlayerElementConstructor<Store>;
|
||||
|
||||
/** Mixin for a complete player element (provider + container). */
|
||||
PlayerMixin: PlayerMixin<Store>;
|
||||
|
||||
/** Mixin that provides player context to descendants. */
|
||||
ProviderMixin: ProviderMixin<Store>;
|
||||
|
||||
@@ -52,15 +43,13 @@ export interface CreatePlayerResult<Store extends PlayerStore> {
|
||||
* import { features } from '@videojs/core/dom';
|
||||
* import { createPlayer, MediaElement } from '@videojs/html';
|
||||
*
|
||||
* const { PlayerElement, PlayerController, context } = createPlayer({
|
||||
* const { ProviderMixin, ContainerMixin, PlayerController, context } = createPlayer({
|
||||
* features: features.video,
|
||||
* });
|
||||
*
|
||||
* // Simple: register pre-composed PlayerElement
|
||||
* customElements.define('video-player', PlayerElement);
|
||||
*
|
||||
* // Custom: extend with PlayerMixin
|
||||
* class MyPlayer extends PlayerMixin(MediaElement) {}
|
||||
* // Provider element: owns the store, provides context to descendants
|
||||
* class VideoPlayer extends ProviderMixin(MediaElement) {}
|
||||
* customElements.define('video-player', VideoPlayer);
|
||||
*
|
||||
* // Control element with selector
|
||||
* class PlayButton extends MediaElement {
|
||||
@@ -98,8 +87,6 @@ export function createPlayer(config: CreatePlayerConfig<AnyPlayerFeature[]>): Cr
|
||||
return createStore<PlayerTarget>()(slice);
|
||||
}
|
||||
|
||||
const PlayerMixin = createPlayerMixin<PlayerStore>(playerContext, create);
|
||||
const PlayerElement = PlayerMixin(MediaElement);
|
||||
const ProviderMixin = createProviderMixin<PlayerStore>(playerContext, create);
|
||||
const ContainerMixin = createContainerMixin<PlayerStore>(playerContext);
|
||||
|
||||
@@ -107,8 +94,6 @@ export function createPlayer(config: CreatePlayerConfig<AnyPlayerFeature[]>): Cr
|
||||
context: playerContext,
|
||||
create,
|
||||
PlayerController,
|
||||
PlayerElement,
|
||||
PlayerMixin,
|
||||
ProviderMixin,
|
||||
ContainerMixin,
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { PlayerStore } from '@videojs/core/dom';
|
||||
import type { MediaElementConstructor } from '@/ui/media-element';
|
||||
import { createContainerMixin } from '../store/container-mixin';
|
||||
import { createProviderMixin } from '../store/provider-mixin';
|
||||
import type { PlayerProviderConstructor } from '../store/types';
|
||||
import type { PlayerContext } from './context';
|
||||
|
||||
type Result<Class extends MediaElementConstructor, Store extends PlayerStore> = Class &
|
||||
PlayerProviderConstructor<Store>;
|
||||
|
||||
export type PlayerMixin<Store extends PlayerStore> = <Class extends MediaElementConstructor>(
|
||||
BaseClass: Class
|
||||
) => Result<Class, Store>;
|
||||
|
||||
/**
|
||||
* Creates a mixin that combines provider and container functionality.
|
||||
*
|
||||
* Use for a complete player element that owns the store and attaches media.
|
||||
*
|
||||
* @param context - Player context for descendant consumption.
|
||||
* @param factory - Factory function that creates a store instance.
|
||||
*/
|
||||
export function createPlayerMixin<Store extends PlayerStore>(
|
||||
context: PlayerContext<Store>,
|
||||
factory: () => Store
|
||||
): PlayerMixin<Store> {
|
||||
const ProviderMixin = createProviderMixin<Store>(context, factory);
|
||||
const ContainerMixin = createContainerMixin<Store>(context);
|
||||
|
||||
return <Class extends MediaElementConstructor>(BaseClass: Class) => {
|
||||
return ProviderMixin(ContainerMixin(BaseClass)) as unknown as Result<Class, Store>;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { MediaElement } from '../../ui/media-element';
|
||||
import { createPlayer } from '../create-player';
|
||||
|
||||
describe('createPlayer', () => {
|
||||
@@ -10,8 +11,6 @@ describe('createPlayer', () => {
|
||||
expect(result.context).toBeDefined();
|
||||
expect(result.create).toBeInstanceOf(Function);
|
||||
expect(result.PlayerController).toBeDefined();
|
||||
expect(result.PlayerElement).toBeDefined();
|
||||
expect(result.PlayerMixin).toBeInstanceOf(Function);
|
||||
expect(result.ProviderMixin).toBeInstanceOf(Function);
|
||||
expect(result.ContainerMixin).toBeInstanceOf(Function);
|
||||
});
|
||||
@@ -25,11 +24,20 @@ describe('createPlayer', () => {
|
||||
expect(store.destroy).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
it('PlayerElement is a valid custom element class', () => {
|
||||
const { PlayerElement } = createPlayer({ features: features.video });
|
||||
it('ProviderMixin produces a valid custom element class', () => {
|
||||
const { ProviderMixin } = createPlayer({ features: features.video });
|
||||
const ProviderElement = ProviderMixin(MediaElement);
|
||||
|
||||
expect(typeof PlayerElement).toBe('function');
|
||||
expect(PlayerElement.prototype).toBeDefined();
|
||||
expect(typeof ProviderElement).toBe('function');
|
||||
expect(ProviderElement.prototype).toBeDefined();
|
||||
});
|
||||
|
||||
it('ContainerMixin produces a valid custom element class', () => {
|
||||
const { ContainerMixin } = createPlayer({ features: features.video });
|
||||
const ContainerElement = ContainerMixin(MediaElement);
|
||||
|
||||
expect(typeof ContainerElement).toBe('function');
|
||||
expect(ContainerElement.prototype).toBeDefined();
|
||||
});
|
||||
|
||||
it('creates audio player with expected exports', () => {
|
||||
@@ -38,8 +46,6 @@ describe('createPlayer', () => {
|
||||
expect(result.context).toBeDefined();
|
||||
expect(result.create).toBeInstanceOf(Function);
|
||||
expect(result.PlayerController).toBeDefined();
|
||||
expect(result.PlayerElement).toBeDefined();
|
||||
expect(result.PlayerMixin).toBeInstanceOf(Function);
|
||||
expect(result.ProviderMixin).toBeInstanceOf(Function);
|
||||
expect(result.ContainerMixin).toBeInstanceOf(Function);
|
||||
});
|
||||
@@ -50,8 +56,6 @@ describe('createPlayer', () => {
|
||||
expect(result.context).toBeDefined();
|
||||
expect(result.create).toBeInstanceOf(Function);
|
||||
expect(result.PlayerController).toBeDefined();
|
||||
expect(result.PlayerElement).toBeDefined();
|
||||
expect(result.PlayerMixin).toBeInstanceOf(Function);
|
||||
expect(result.ProviderMixin).toBeInstanceOf(Function);
|
||||
expect(result.ContainerMixin).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user