mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(store): rename feature to slice (#373)
This commit is contained in:
@@ -2,12 +2,16 @@
|
||||
|
||||
// Re-exports from core/dom
|
||||
export {
|
||||
type AnyPlayerFeature,
|
||||
type BufferState,
|
||||
definePlayerFeature,
|
||||
type FeatureAvailability,
|
||||
features,
|
||||
type Media,
|
||||
type MediaContainer,
|
||||
type PlaybackState,
|
||||
type PlayerFeature,
|
||||
type PlayerStore,
|
||||
type PlayerTarget,
|
||||
type SourceState,
|
||||
selectBuffer,
|
||||
@@ -19,13 +23,9 @@ export {
|
||||
type VolumeState,
|
||||
} from '@videojs/core/dom';
|
||||
|
||||
export type { AnyFeature, Feature, InferFeatureState } from '@videojs/store';
|
||||
|
||||
// Re-exports (for custom features)
|
||||
export { createFeatureSelector, defineFeature } from '@videojs/store';
|
||||
export type { Comparator, Selector } from '@videojs/store/react';
|
||||
|
||||
// Re-exports (for advanced store access)
|
||||
// Re-exports from store (utilities)
|
||||
export type { Comparator, Selector } from '@videojs/store';
|
||||
export { createSelector, shallowEqual } from '@videojs/store';
|
||||
export { useSelector, useStore } from '@videojs/store/react';
|
||||
|
||||
// Media primitives
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import type { Media, PlayerTarget } from '@videojs/core/dom';
|
||||
import type { AnyFeature, FeatureStore, UnionFeatureState } from '@videojs/store';
|
||||
import { createStore } from '@videojs/store';
|
||||
import type { AnyPlayerFeature, Media, PlayerStore, PlayerTarget } from '@videojs/core/dom';
|
||||
import type { UnionSliceState } from '@videojs/store';
|
||||
import { combine, createStore } from '@videojs/store';
|
||||
import { useStore } from '@videojs/store/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Container, PlayerContextProvider, useMedia, usePlayerContext } from './context';
|
||||
|
||||
export interface CreatePlayerConfig<Features extends AnyFeature[]> {
|
||||
export interface CreatePlayerConfig<Features extends AnyPlayerFeature[]> {
|
||||
features: Features;
|
||||
displayName?: string;
|
||||
}
|
||||
@@ -18,26 +18,26 @@ export interface ProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export interface CreatePlayerResult<Features extends AnyFeature[]> {
|
||||
Provider: (props: ProviderProps) => ReactNode;
|
||||
export interface CreatePlayerResult<Features extends AnyPlayerFeature[]> {
|
||||
Provider: FC<ProviderProps>;
|
||||
Container: typeof Container;
|
||||
usePlayer: UsePlayerHook<Features>;
|
||||
useMedia: () => Media | null;
|
||||
}
|
||||
|
||||
type UsePlayerHook<Features extends AnyFeature[]> = {
|
||||
(): FeatureStore<Features>;
|
||||
<R>(selector: (state: UnionFeatureState<Features>) => R): R;
|
||||
type UsePlayerHook<Features extends AnyPlayerFeature[]> = {
|
||||
(): PlayerStore<Features>;
|
||||
<R>(selector: (state: UnionSliceState<Features>) => R): R;
|
||||
};
|
||||
|
||||
export function createPlayer<const Features extends AnyFeature<PlayerTarget>[]>(
|
||||
export function createPlayer<const Features extends AnyPlayerFeature[]>(
|
||||
config: CreatePlayerConfig<Features>
|
||||
): CreatePlayerResult<Features> {
|
||||
type Store = FeatureStore<Features>;
|
||||
type State = UnionFeatureState<Features>;
|
||||
type Store = PlayerStore<Features>;
|
||||
type State = UnionSliceState<Features>;
|
||||
|
||||
function Provider({ children }: ProviderProps): ReactNode {
|
||||
const [store] = useState(() => createStore({ features: config.features }));
|
||||
const [store] = useState(() => createStore<PlayerTarget>()(combine(...config.features)));
|
||||
const [media, setMedia] = useState<Media | null>(null);
|
||||
|
||||
useEffect(() => () => store.destroy(), [store]);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { render, renderHook } from '@testing-library/react';
|
||||
import { defineFeature } from '@videojs/store';
|
||||
import type { PlayerStore } from '@videojs/core/dom';
|
||||
import { defineSlice } from '@videojs/store';
|
||||
import type { ReactNode } from 'react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { createPlayer } from '../create-player';
|
||||
|
||||
describe('createPlayer', () => {
|
||||
// Create a mock feature that works with any target
|
||||
const mockFeature = defineFeature<any>()({
|
||||
// Create a mock slice that works with any target
|
||||
const mockSlice = defineSlice()({
|
||||
state: () => ({
|
||||
volume: 1,
|
||||
muted: false,
|
||||
@@ -17,9 +17,9 @@ describe('createPlayer', () => {
|
||||
|
||||
describe('Provider', () => {
|
||||
it('creates store on mount', () => {
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
let store: unknown;
|
||||
let store!: PlayerStore;
|
||||
|
||||
function TestComponent() {
|
||||
store = usePlayer();
|
||||
@@ -33,15 +33,15 @@ describe('createPlayer', () => {
|
||||
);
|
||||
|
||||
expect(store).toBeDefined();
|
||||
expect(typeof (store as any).subscribe).toBe('function');
|
||||
expect(typeof (store as any).attach).toBe('function');
|
||||
expect(typeof (store as any).destroy).toBe('function');
|
||||
expect(typeof store.subscribe).toBe('function');
|
||||
expect(typeof store.attach).toBe('function');
|
||||
expect(typeof store.destroy).toBe('function');
|
||||
});
|
||||
|
||||
it('destroys store on unmount', () => {
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
let store: any;
|
||||
let store!: PlayerStore;
|
||||
|
||||
function TestComponent() {
|
||||
store = usePlayer();
|
||||
@@ -62,15 +62,15 @@ describe('createPlayer', () => {
|
||||
|
||||
it('uses displayName when provided', () => {
|
||||
const { Provider } = createPlayer({
|
||||
features: [mockFeature] as any,
|
||||
features: [mockSlice],
|
||||
displayName: 'VideoPlayer',
|
||||
});
|
||||
|
||||
expect((Provider as any).displayName).toBe('VideoPlayer.Provider');
|
||||
expect(Provider.displayName).toBe('VideoPlayer.Provider');
|
||||
});
|
||||
|
||||
it('renders children', () => {
|
||||
const { Provider } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
const { container } = render(
|
||||
<Provider>
|
||||
@@ -84,7 +84,7 @@ describe('createPlayer', () => {
|
||||
|
||||
describe('usePlayer', () => {
|
||||
it('returns store without selector', () => {
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => <Provider>{children}</Provider>;
|
||||
|
||||
@@ -96,7 +96,7 @@ describe('createPlayer', () => {
|
||||
});
|
||||
|
||||
it('returns selected state with selector', () => {
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider, usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => <Provider>{children}</Provider>;
|
||||
|
||||
@@ -106,7 +106,7 @@ describe('createPlayer', () => {
|
||||
});
|
||||
|
||||
it('throws outside Provider', () => {
|
||||
const { usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
@@ -120,16 +120,16 @@ describe('createPlayer', () => {
|
||||
|
||||
describe('Container', () => {
|
||||
it('is exported from createPlayer result', () => {
|
||||
const { Container } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Container } = createPlayer({ features: [mockSlice] });
|
||||
expect(Container).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('full integration', () => {
|
||||
it('Provider → Container → media attach flow', () => {
|
||||
const { Provider, Container, usePlayer } = createPlayer({ features: [mockFeature] as any });
|
||||
const { Provider, Container, usePlayer } = createPlayer({ features: [mockSlice] });
|
||||
|
||||
let store: any;
|
||||
let store!: PlayerStore;
|
||||
|
||||
function TestComponent() {
|
||||
store = usePlayer();
|
||||
|
||||
Reference in New Issue
Block a user