feat(html): reorganize import paths by use case (#480)

This commit is contained in:
rahim
2026-02-09 20:49:42 +11:00
committed by GitHub
parent 5f5ffb9f0a
commit 870cbb77e4
18 changed files with 178 additions and 84 deletions
+2 -2
View File
@@ -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
View File
@@ -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(
+5
View File
@@ -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 = [];
+14 -9
View File
@@ -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",
+18
View File
@@ -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;
}
}
+1
View File
@@ -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
-17
View File
@@ -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;
}
}
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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() {