mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 21:57:29 +00:00
feat(html): reorganize import paths by use case (#480)
This commit is contained in:
@@ -1384,12 +1384,12 @@ Items identified during planning but deferred from initial implementation.
|
||||
|
||||
### Side-Effect Registration System
|
||||
|
||||
The `/ui/*`, `/skin/*`, `/feature/*` exports provide scaffold for future registration patterns:
|
||||
The `/ui/*`, `/video/*`, `/audio/*`, `/background/*`, `/feature/*` exports provide scaffold for future registration patterns:
|
||||
|
||||
```ts
|
||||
// User imports trigger registration
|
||||
import '@videojs/html/ui/play-button';
|
||||
import '@videojs/html/skin/modern';
|
||||
import '@videojs/html/video/skin';
|
||||
import '@videojs/html/feature/quality-selection';
|
||||
```
|
||||
|
||||
|
||||
+2
-2
@@ -327,7 +327,7 @@ ElementName.define('custom-name');
|
||||
|
||||
// With custom mixin
|
||||
import { createStore } from '@videojs/store/lit';
|
||||
import { extendConfig } from '@videojs/html/skins/frosted';
|
||||
import { extendConfig } from '@videojs/html/video/skin';
|
||||
|
||||
const { StoreMixin } = createStore(
|
||||
extendConfig({ features: [customFeature] })
|
||||
@@ -384,7 +384,7 @@ ElementName.define('custom-name', StoreMixin);
|
||||
### Extending
|
||||
|
||||
import { createStore } from '@videojs/store/lit';
|
||||
import { extendConfig, FrostedSkinElement } from '@videojs/html/skins/frosted';
|
||||
import { extendConfig, FrostedSkinElement } from '@videojs/html/video/skin';
|
||||
import { chaptersFeature } from './features/chapters';
|
||||
|
||||
const { StoreMixin } = createStore(
|
||||
|
||||
@@ -51,6 +51,11 @@ export type AudioFeatures = [
|
||||
PlayerFeature<MediaBufferState>,
|
||||
];
|
||||
|
||||
// TODO: Define background video features (e.g., playback, source, buffer)
|
||||
export type BackgroundFeatures = [];
|
||||
|
||||
export type VideoPlayerStore = PlayerStore<VideoFeatures>;
|
||||
|
||||
export type AudioPlayerStore = PlayerStore<AudioFeatures>;
|
||||
|
||||
export type BackgroundPlayerStore = PlayerStore<BackgroundFeatures>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AudioFeatures, VideoFeatures } from '../../media/types';
|
||||
import type { AudioFeatures, BackgroundFeatures, VideoFeatures } from '../../media/types';
|
||||
import { bufferFeature } from './buffer';
|
||||
import { fullscreenFeature } from './fullscreen';
|
||||
import { pipFeature } from './pip';
|
||||
@@ -29,3 +29,6 @@ export const video: VideoFeatures = [
|
||||
];
|
||||
|
||||
export const audio: AudioFeatures = [playbackFeature, volumeFeature, timeFeature, sourceFeature, bufferFeature];
|
||||
|
||||
// TODO: Add background video features (e.g., playback, source, buffer)
|
||||
export const background: BackgroundFeatures = [];
|
||||
|
||||
@@ -18,21 +18,26 @@
|
||||
"development": "./dist/dev/index.js",
|
||||
"default": "./dist/default/index.js"
|
||||
},
|
||||
"./player/*": {
|
||||
"types": "./dist/dev/define/player/*.d.ts",
|
||||
"development": "./dist/dev/define/player/*.js",
|
||||
"default": "./dist/default/define/player/*.js"
|
||||
"./video/*": {
|
||||
"types": "./dist/dev/define/video/*.d.ts",
|
||||
"development": "./dist/dev/define/video/*.js",
|
||||
"default": "./dist/default/define/video/*.js"
|
||||
},
|
||||
"./audio/*": {
|
||||
"types": "./dist/dev/define/audio/*.d.ts",
|
||||
"development": "./dist/dev/define/audio/*.js",
|
||||
"default": "./dist/default/define/audio/*.js"
|
||||
},
|
||||
"./background/*": {
|
||||
"types": "./dist/dev/define/background/*.d.ts",
|
||||
"development": "./dist/dev/define/background/*.js",
|
||||
"default": "./dist/default/define/background/*.js"
|
||||
},
|
||||
"./ui/*": {
|
||||
"types": "./dist/dev/define/ui/*.d.ts",
|
||||
"development": "./dist/dev/define/ui/*.js",
|
||||
"default": "./dist/default/define/ui/*.js"
|
||||
},
|
||||
"./skin/*": {
|
||||
"types": "./dist/dev/define/skin/*.d.ts",
|
||||
"development": "./dist/dev/define/skin/*.js",
|
||||
"default": "./dist/default/define/skin/*.js"
|
||||
},
|
||||
"./feature/*": {
|
||||
"types": "./dist/dev/define/feature/*.d.ts",
|
||||
"development": "./dist/dev/define/feature/*.js",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { createPlayer } from '../../player/create-player';
|
||||
|
||||
const { PlayerElement } = createPlayer({
|
||||
features: features.audio,
|
||||
});
|
||||
|
||||
export class AudioPlayerElement extends PlayerElement {
|
||||
static readonly tagName = 'audio-player';
|
||||
}
|
||||
|
||||
customElements.define(AudioPlayerElement.tagName, AudioPlayerElement);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[AudioPlayerElement.tagName]: AudioPlayerElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// TODO: Implement AudioSkinElement and then register it here
|
||||
@@ -0,0 +1,18 @@
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { createPlayer } from '../../player/create-player';
|
||||
|
||||
const { PlayerElement } = createPlayer({
|
||||
features: features.background,
|
||||
});
|
||||
|
||||
export class BackgroundVideoPlayerElement extends PlayerElement {
|
||||
static readonly tagName = 'background-video-player';
|
||||
}
|
||||
|
||||
customElements.define(BackgroundVideoPlayerElement.tagName, BackgroundVideoPlayerElement);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[BackgroundVideoPlayerElement.tagName]: BackgroundVideoPlayerElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// TODO: Implement BackgroundVideoSkinElement and then register it here
|
||||
@@ -1,17 +0,0 @@
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { MediaElement } from '@/ui/media-element';
|
||||
import { createPlayer } from '../../player/create-player';
|
||||
|
||||
const { PlayerMixin } = createPlayer({
|
||||
features: features.video,
|
||||
});
|
||||
|
||||
export class VideoPlayer extends PlayerMixin(MediaElement) {}
|
||||
|
||||
customElements.define('video-player', VideoPlayer);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'video-player': VideoPlayer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { createPlayer } from '../../player/create-player';
|
||||
|
||||
const { PlayerElement } = createPlayer({
|
||||
features: features.video,
|
||||
});
|
||||
|
||||
export class VideoPlayerElement extends PlayerElement {
|
||||
static readonly tagName = 'video-player';
|
||||
}
|
||||
|
||||
customElements.define(VideoPlayerElement.tagName, VideoPlayerElement);
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
[VideoPlayerElement.tagName]: VideoPlayerElement;
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,12 @@ describe('createPlayer', () => {
|
||||
assertType<string[]>(store.events);
|
||||
});
|
||||
|
||||
it('resolves background features to generic PlayerStore', () => {
|
||||
const result = createPlayer({ features: features.background });
|
||||
|
||||
assertType<CreatePlayerResult<PlayerStore<[]>>>(result);
|
||||
});
|
||||
|
||||
it('resolves extended audio features to generic PlayerStore', () => {
|
||||
interface AnalyticsState {
|
||||
events: string[];
|
||||
|
||||
@@ -31,4 +31,28 @@ describe('createPlayer', () => {
|
||||
expect(typeof PlayerElement).toBe('function');
|
||||
expect(PlayerElement.prototype).toBeDefined();
|
||||
});
|
||||
|
||||
it('creates audio player with expected exports', () => {
|
||||
const result = createPlayer({ features: features.audio });
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('creates background player with expected exports', () => {
|
||||
const result = createPlayer({ features: features.background });
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
+21
-21
@@ -29,7 +29,7 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -44,11 +44,11 @@ import '@videojs/html/player/video';
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import '@videojs/react/skin/video.css';
|
||||
import '@videojs/react/video/skin.css';
|
||||
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { VideoSkin } from '@videojs/react/skin/video';
|
||||
import { VideoSkin } from '@videojs/react/video/skin';
|
||||
|
||||
const { Provider: VideoProvider } = createPlayer({
|
||||
features: [...features.video],
|
||||
@@ -68,9 +68,9 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -88,11 +88,11 @@ Skin detects chapters feature, shows chapter menu.
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import '@videojs/react/skin/video.css';
|
||||
import '@videojs/react/video/skin.css';
|
||||
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { features, chaptersSlice } from '@videojs/core/dom';
|
||||
import { VideoSkin } from '@videojs/react/skin/video';
|
||||
import { VideoSkin } from '@videojs/react/video/skin';
|
||||
|
||||
const { Provider: VideoProvider } = createPlayer({
|
||||
features: [...features.video, chaptersSlice],
|
||||
@@ -112,10 +112,10 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/feature/chapters';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -133,12 +133,12 @@ Skin detects streaming features, shows quality/tracks menus.
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import '@videojs/react/skin/video.css';
|
||||
import '@videojs/react/video/skin.css';
|
||||
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { HlsVideo } from '@videojs/react/media/hls';
|
||||
import { VideoSkin } from '@videojs/react/skin/video';
|
||||
import { VideoSkin } from '@videojs/react/video/skin';
|
||||
|
||||
const { Provider: VideoProvider } = createPlayer({
|
||||
features: [...features.video, ...features.streaming],
|
||||
@@ -158,11 +158,11 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/feature/streaming';
|
||||
import '@videojs/html/media/hls-video';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -180,12 +180,12 @@ Same skin adapts to show ad UI.
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import '@videojs/react/skin/video.css';
|
||||
import '@videojs/react/video/skin.css';
|
||||
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { HlsVideo } from '@videojs/react/media/hls';
|
||||
import { VideoSkin } from '@videojs/react/skin/video';
|
||||
import { VideoSkin } from '@videojs/react/video/skin';
|
||||
|
||||
const { Provider: VideoProvider } = createPlayer({
|
||||
features: [...features.video, ...features.streaming, ...features.ads],
|
||||
@@ -205,12 +205,12 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/feature/streaming';
|
||||
import '@videojs/html/feature/ads';
|
||||
import '@videojs/html/media/hls-video';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
## 6. Full Custom (Escape Hatch)
|
||||
|
||||
+29
-26
@@ -21,12 +21,30 @@ Only fundamentally different behaviors get their own player:
|
||||
|
||||
## Import Paths
|
||||
|
||||
### Player (includes base features)
|
||||
Grouped by use case, then by concern:
|
||||
|
||||
### Video (default)
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video'; // includes features.video
|
||||
import '@videojs/html/player/audio'; // includes features.audio
|
||||
import '@videojs/html/player/background-video';
|
||||
import '@videojs/html/video/player'; // includes features.video
|
||||
import '@videojs/html/video/skin';
|
||||
import '@videojs/html/video/skin.css';
|
||||
```
|
||||
|
||||
### Audio (default)
|
||||
|
||||
```ts
|
||||
import '@videojs/html/audio/player'; // includes features.audio
|
||||
import '@videojs/html/audio/skin';
|
||||
import '@videojs/html/audio/skin.css';
|
||||
```
|
||||
|
||||
### Background Video
|
||||
|
||||
```ts
|
||||
import '@videojs/html/background/player'; // includes features.background
|
||||
import '@videojs/html/background/skin';
|
||||
import '@videojs/html/background/skin.css';
|
||||
```
|
||||
|
||||
### Features (additive)
|
||||
@@ -51,21 +69,6 @@ import '@videojs/html/media/hls-audio';
|
||||
import '@videojs/html/media/dash-video';
|
||||
```
|
||||
|
||||
### Skins
|
||||
|
||||
```ts
|
||||
// Default skin
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
|
||||
// Named variants
|
||||
import '@videojs/html/skin/video/minimal.css';
|
||||
import '@videojs/html/skin/video/minimal';
|
||||
|
||||
import '@videojs/html/skin/audio.css';
|
||||
import '@videojs/html/skin/audio';
|
||||
```
|
||||
|
||||
### UI Primitives (for custom skins)
|
||||
|
||||
```ts
|
||||
@@ -81,8 +84,8 @@ import '@videojs/html/ui/slider';
|
||||
Elements register globally when imported:
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -165,9 +168,9 @@ customElements.define('video-player', PlayerElement);
|
||||
Default skin adapts to available features:
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/feature/streaming'; // skin will show quality menu
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
@@ -351,11 +354,11 @@ When media element and container need different DOM locations:
|
||||
|
||||
```ts
|
||||
// main.ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/feature/streaming';
|
||||
import '@videojs/html/media/hls-video';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
|
||||
@@ -74,11 +74,11 @@ Start simple Add as needed
|
||||
### React
|
||||
|
||||
```tsx
|
||||
import '@videojs/react/skin/video.css';
|
||||
import '@videojs/react/video/skin.css';
|
||||
|
||||
import { createPlayer } from '@videojs/react';
|
||||
import { features } from '@videojs/core/dom';
|
||||
import { VideoSkin } from '@videojs/react/skin/video';
|
||||
import { VideoSkin } from '@videojs/react/video/skin';
|
||||
|
||||
const { Provider: VideoProvider } = createPlayer({
|
||||
features: [...features.video],
|
||||
@@ -98,9 +98,9 @@ function App() {
|
||||
### HTML
|
||||
|
||||
```ts
|
||||
import '@videojs/html/player/video';
|
||||
import '@videojs/html/skin/video.css';
|
||||
import '@videojs/html/skin/video';
|
||||
import '@videojs/html/video/player';
|
||||
import '@videojs/html/video/skin.css';
|
||||
import '@videojs/html/video/skin';
|
||||
```
|
||||
|
||||
```html
|
||||
|
||||
@@ -79,8 +79,17 @@ function generateHTMLCode(useCase: UseCase, skin: Skin, renderer: Renderer, play
|
||||
</${providerTag}>`;
|
||||
}
|
||||
|
||||
function getSkinImportPath(skin: Skin): string {
|
||||
const map: Record<Skin, string> = {
|
||||
'default-video': '@videojs/html/video/skin',
|
||||
'default-audio': '@videojs/html/audio/skin',
|
||||
minimal: '@videojs/html/video/minimal-skin',
|
||||
};
|
||||
return map[skin];
|
||||
}
|
||||
|
||||
function generateJS(skin: Skin): string {
|
||||
return `import '@videojs/html/skins/${skin}';`;
|
||||
return `import '${getSkinImportPath(skin)}';`;
|
||||
}
|
||||
|
||||
export default function HTMLUsageCodeBlock() {
|
||||
|
||||
Reference in New Issue
Block a user