refactor(html): context-based media discovery, remove slot="media" (#997)

This commit is contained in:
rahim
2026-03-18 22:21:30 -07:00
committed by GitHub
parent 81781ca585
commit e09ea334bd
24 changed files with 158 additions and 21 deletions
@@ -37,7 +37,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="${root}">
<slot name="media"></slot>
<slot></slot>
<div class="${controls}">
<media-tooltip-group>
@@ -21,7 +21,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="media-minimal-skin media-minimal-skin--audio">
<slot name="media"></slot>
<slot></slot>
<div class="media-controls">
<media-tooltip-group>
@@ -36,7 +36,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="${root}">
<slot name="media"></slot>
<slot></slot>
<div class="${controls}">
<media-tooltip-group>
+1 -1
View File
@@ -21,7 +21,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="media-default-skin media-default-skin--audio">
<slot name="media"></slot>
<slot></slot>
<div class="media-surface media-controls">
<media-tooltip-group>
+1 -1
View File
@@ -11,7 +11,7 @@ background-video-skin {
object-fit: var(--media-object-fit);
}
background-video-skin > [slot="media"] {
background-video-skin > background-video {
position: absolute;
inset: 0;
width: 100%;
+1 -1
View File
@@ -5,7 +5,7 @@ import { safeDefine } from '../safe-define';
function getTemplateHTML(_attrs: Record<string, string>) {
return /*html*/ `
<media-container>
<slot name="media" slot="media"></slot>
<slot></slot>
</media-container>
`;
}
@@ -47,7 +47,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="${root(true)}">
<slot name="media"></slot>
<slot></slot>
<media-poster class="${poster(true)}">
<slot name="poster"></slot>
@@ -26,7 +26,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="media-minimal-skin media-minimal-skin--video">
<slot name="media"></slot>
<slot></slot>
<media-poster>
<slot name="poster"></slot>
@@ -46,7 +46,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="${root(true)}">
<slot name="media"></slot>
<slot></slot>
<media-poster class="${poster(true)}">
<slot name="poster"></slot>
+1 -1
View File
@@ -27,7 +27,7 @@ const SEEK_TIME = 10;
function getTemplateHTML() {
return /*html*/ `
<media-container class="media-default-skin media-default-skin--video">
<slot name="media"></slot>
<slot></slot>
<media-poster>
<slot name="poster"></slot>
+1
View File
@@ -11,6 +11,7 @@ export * from './player/context';
export * from './player/create-player';
export * from './player/player-controller';
export * from './store/container-mixin';
export * from './store/media-attach-mixin';
export * from './store/provider-mixin';
export * from './store/types';
// UI Components
@@ -1,4 +1,6 @@
import type { Media } from '@videojs/core/dom';
import { namedNodeMapToObject } from '@videojs/utils/dom';
import { MediaAttachMixin } from '../../store/media-attach-mixin';
function getTemplateHTML(attrs: Record<string, string>) {
return /*html*/ `
@@ -22,7 +24,7 @@ function getTemplateHTML(attrs: Record<string, string>) {
}
// Don't extend CustomMediaMixin to save some bytes, background videos don't need the full Media API.
export class BackgroundVideo extends HTMLElement {
export class BackgroundVideo extends MediaAttachMixin(HTMLElement) {
static shadowRootOptions = { mode: 'open' as ShadowRootMode };
static getTemplateHTML = getTemplateHTML;
static get observedAttributes() {
@@ -54,6 +56,11 @@ export class BackgroundVideo extends HTMLElement {
this.target!.muted = !this.hasAttribute('nomuted');
}
// Register the inner <video> (not `this`) with the provider.
getMediaTarget(): Media | null {
return this.target;
}
attributeChangedCallback(attrName: string, oldValue: string | null, newValue: string | null): void {
if (attrName === 'src' && oldValue !== newValue) {
this.target!.src = newValue ?? '';
+2 -1
View File
@@ -1,6 +1,7 @@
import { DashCustomMedia } from '@videojs/core/dom/media/dash';
import { MediaAttachMixin } from '../../store/media-attach-mixin';
export class DashVideo extends DashCustomMedia {
export class DashVideo extends MediaAttachMixin(DashCustomMedia) {
static getTemplateHTML(attrs: Record<string, string>): string {
const { src, ...rest } = attrs;
// biome-ignore lint/complexity/noThisInStatic: intentional use of super
+2 -1
View File
@@ -1,6 +1,7 @@
import { HlsCustomMedia } from '@videojs/core/dom/media/hls';
import { MediaAttachMixin } from '../../store/media-attach-mixin';
export class HlsVideo extends HlsCustomMedia {
export class HlsVideo extends MediaAttachMixin(HlsCustomMedia) {
static getTemplateHTML(attrs: Record<string, string>): string {
const { src, ...rest } = attrs;
// biome-ignore lint/complexity/noThisInStatic: intentional use of super
@@ -1,6 +1,7 @@
import { SimpleHlsCustomMedia } from '@videojs/core/dom/media/simple-hls';
import { MediaAttachMixin } from '../../store/media-attach-mixin';
export class SimpleHlsVideo extends SimpleHlsCustomMedia {
export class SimpleHlsVideo extends MediaAttachMixin(SimpleHlsCustomMedia) {
static getTemplateHTML(attrs: Record<string, string>): string {
const { src, ...rest } = attrs;
// biome-ignore lint/complexity/noThisInStatic: intentional use of super
@@ -0,0 +1,60 @@
import type { Media } from '@videojs/core/dom';
import { ContextEvent } from '@videojs/element/context';
import type { CustomElement } from '@videojs/utils/dom';
import type { AnyConstructor, Constructor } from '@videojs/utils/types';
import { type MediaAttachContext, mediaAttachContext } from '../player/context';
export type MediaAttachMixin = <Class extends AnyConstructor<HTMLElement>>(BaseClass: Class) => Class;
/**
* Create a mixin that consumes `mediaAttachContext` and registers the
* element as the media with the provider.
*
* Uses the raw context-request protocol so it works with any
* `HTMLElement` subclass — no `ReactiveControllerHost` required.
*
* @param context - The media attach context to consume.
*/
export function createMediaAttachMixin(context: MediaAttachContext): MediaAttachMixin {
return <Class extends AnyConstructor<HTMLElement>>(BaseClass: Class) => {
class MediaAttachElement extends (BaseClass as unknown as Constructor<CustomElement>) {
#setMedia: ((media: Media | null) => void) | null = null;
#unsubscribe: (() => void) | null = null;
getMediaTarget(): Media | null {
return this as unknown as Media;
}
override connectedCallback() {
super.connectedCallback?.();
this.dispatchEvent(
new ContextEvent(
context,
this,
(value, unsubscribe) => {
if (unsubscribe) this.#unsubscribe = unsubscribe;
this.#setMedia = value ?? null;
if (this.isConnected) {
this.#setMedia?.(this.getMediaTarget());
}
},
true
)
);
}
override disconnectedCallback() {
super.disconnectedCallback?.();
this.#setMedia?.(null);
this.#unsubscribe?.();
this.#unsubscribe = null;
this.#setMedia = null;
}
}
return MediaAttachElement as unknown as Class;
};
}
export const MediaAttachMixin = createMediaAttachMixin(mediaAttachContext);