feat(html): add createMediaElement - #2603
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@videojs/cdn
@videojs/core
@videojs/element
@videojs/html
@videojs/media
@videojs/react
@videojs/spf
@videojs/store
@videojs/utils
@videojs/cloudflare-video
@videojs/dash-video
@videojs/hlsjs-video
@videojs/mux-audio
@videojs/mux-video
@videojs/native-hls-video
@videojs/shaka-video
@videojs/spotify-audio
@videojs/tiktok-video
@videojs/twitch-video
@videojs/vimeo-video
@videojs/wistia-video
@videojs/youtube-video
@videojs/google-cast
@videojs/mux-data
commit: |
📦 Bundle Size Report🎨 @videojs/html
Small changes (32, ≤ 300 B)
Presets (7)
Media (18)
Players (5)
Skins (29)
UI Components (62)
⚛️ @videojs/react — 7 small size changes
Presets (7)
Media (22)
Extensions (2)
Players (5)
Skins (18)
UI Components (39)
🧩 @videojs/core — no changesEntries (76)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (13)
📦 @videojs/cdn — no changes📦 @videojs/cloudflare-video — 1 small size change
📦 @videojs/dash-video — 1 small size change
📦 @videojs/google-cast — no changes📦 @videojs/hlsjs-video — 1 small size change
📦 @videojs/media
Entries (3)
📦 @videojs/mux — no changes📦 @videojs/mux-audio — 2 small size changes
Entries (2)
📦 @videojs/mux-data — no changes📦 @videojs/mux-video — 2 small size changes
Entries (2)
📦 @videojs/native-hls-video — 1 small size change
📦 @videojs/shaka-video — 1 small size change
📦 @videojs/spf — 2 small size changes
Entries (7)
📦 @videojs/spotify-audio — 1 small size change
📦 @videojs/tiktok-video — 1 small size change
📦 @videojs/twitch-video — 1 small size change
📦 @videojs/vimeo-video — 1 small size change
📦 @videojs/wistia-video — 2 small size changes
Entries (2)
📦 @videojs/youtube-video — 1 small size change
ℹ️ How to interpretEach entry is independently bundled, minified, and brotli-compressed. Initial size includes its static import graph; lazy dynamic chunks are reported separately. Entries are not additive because their dependency graphs overlap. Preset rows represent realistic combined bundles. Changes of 300 B or less across initial, lazy, and total size are collapsed, not discarded. Run |
b5df700 to
0fcf878
Compare
0fcf878 to
1ed7237
Compare
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
1ed7237 to
6ef7386
Compare
6ef7386 to
1868310
Compare
1868310 to
b328d86
Compare
b328d86 to
4dac066
Compare
4dac066 to
bf58712
Compare
`CustomMediaElement(Adapter)` reads the element it renders and attaches to
from the adapter's static `host` ('video' | 'audio' | 'iframe') instead of a
tag argument. `HTMLVideoAdapter` and `HTMLAudioAdapter` declare theirs, the
iframe embed adapters declare 'iframe', and the hls.js-backed
`MuxAudioAdapter` overrides the video base it extends.
`createMediaElement` drops its `tag` option, leaving `template` as the only
knob, and the element's static `getTemplateHTML` is renamed `template`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The API-docs builder follows the host class chain, through mixin calls and hoisted bases, to its static `host` or the `HTMLVideoAdapter` / `HTMLAudioAdapter` root, now that neither factory names the target. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b034e90. Configure here.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define file exports `SpotifyAudioElement`, so the `endsWith('Audio')`
check has to run on the suffix-stripped name. Adds an `EmbedAudio` fixture
so the iframe-audio path stays covered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` is `'video' | 'audio'` so the hls.js `MuxAudioAdapter` override still fits, and every other adapter keeps the literal, so façades can read the host off the type as well as the value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
It shadowed the `PlaybackAdapter` interface; the builder fixture stub mirrors the name and types the static `host` it reads. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…clared `streamType` is settable on every media adapter, but the DASH and SPF audio adapters left it out of `defaultProps`, so neither `<dash-video stream-type>` nor the React `streamType` prop reached them by contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An adapter declares two statics, `host` and `defaultProps`, and the element
derives everything else from them. Each primitive default becomes a content
attribute of that type, named by its WHATWG spelling or kebab-case, and reset
to the default when removed; objects stay property-only. The base adapters
declare the attributes their native element accepts (`videoContentAttributes`,
`audioContentAttributes`), which pass through to the inner element; an iframe
passes nothing. `static properties` is gone.
`CustomMediaElement(Adapter, { template? })` is now a thin composition of
pieces that stand on their own: `renderHost`, `AdapterAttachment`,
`forwardAdapter` / `reflectAttributes` / `bridgeEvent`, and `MediaChildren`.
Definition state is per class and the light-DOM target lookup only adopts
direct children.
BREAKING CHANGE: `CustomMediaElement` no longer takes a tag or exposes
`static properties`; adapters must declare `static host` and
`static defaultProps`. Audio elements drop the video-only attributes and
embeds reflect only the attributes their adapter declares.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The builder reads the native attribute lists beside the base adapters and derives custom attributes from each adapter's `defaultProps`, the same rule the element applies, instead of parsing `static properties`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eces `<background-video>` renders through `renderHost` and `<hls-background-video>` keeps its engine attached and destroyed through `AdapterAttachment`, instead of each hand-rolling the shadow root and the deferred teardown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
c53ab8e to
9c53e38
Compare
The fixture stub asked for an adapter with `host` and `defaultProps`; the fixture base adapters only had `host` where the builder needed it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The `create-media-component` reference lands in #2604, so linking it here broke the site build for this PR alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The html façades follow the UI elements: `media/<name>/element.ts` exports `<Name>Element` carrying its static `tagName`, and the define entry only registers it. `<mux-background-video>` is a subclass of `HlsBackgroundVideoElement` with its own tag, since one class can hold one tag name. BREAKING CHANGE: `HlsJsVideo`, `SpotifyAudio`, and the other html media classes are now `HlsJsVideoElement`, `SpotifyAudioElement`, and so on, and declare their own `tagName`. `@videojs/html/media/<name>` keeps exporting the element class. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The define entries no longer declare a class, so the builder resolves what they pass to `safeDefine()` to the element class in the media directory and reads its `tagName` and composition there. Fixtures follow the element.ts layout, including a flavour reached through a nested barrel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`renderHost(element, attrs)` takes the template and shadow root options from the element's own constructor, so callers stop passing what the class already declares. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Six embeds each carried the same `<style>` and `<iframe>` markup and the same attribute-to-props mapper. `embedTemplate` renders the frame from what differs (URL, feature policy, extra attributes, host sizing, the rule for a host without controls), and `propsFromAttributes` in `@videojs/media/dom` reads the initial attributes through the adapter's `defaultProps`, the same coercion the element applies later. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`HTMLVideoAdapter.host` was typed `'video' | 'audio'` only so the hls.js `MuxAudioAdapter`, which extends the video adapter, could override it. That made every video adapter's `host` a union in the types. The base is `'video'` again and the Mux audio adapter casts its inverted base to drop the literal, the same way `MuxBackgroundVideoElement` does for `tagName`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Attributes: - A boolean that defaults to `true` stays property-only. An absent HTML boolean attribute cannot mean `true`, so `<x playsinline>` could never turn it off and `el.playsInline = false` did nothing. - A property from `defaultProps` owns its attribute by definition, so an adapter written with fields rather than accessors works; one exposed read-only only reflects, with a development warning. - `defaultMuted` owns the `muted` attribute, as in HTML, and seeds the `muted` state with it. It used to write `muted` instead, so an embed that stores the two apart built an unmuted URL after `el.defaultMuted = true`. - A number that does not parse falls back to the default. - A target that arrives later gets every attribute already on the element replayed, and only a direct child of the host tag is adopted as `media`. Surface: - Forwarding copies `enumerable`, treats underscore-prefixed members as private, keeps `title` for the element, and reports other collisions in development. - The event bridge forwards composed events an adapter raises itself, skipping only the adapter's copies of events its target dispatched, keeps a stable handler, and is torn down with the adapter. - `MediaChildren` removes an attribute from the clone when it leaves the child, and can disconnect. - `@videojs/media/dom` exports only the deliberate extension points. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

What this does
Adds
createMediaElement, the html façade factory, and builds the built-in media elements with it.Each html façade lives in
media/<name>/element.tsas<Name>Elementwith its owntagName, like the UI elements; thedefine/mediaentry only registers it.The adapter contract
An adapter declares two statics and the element derives everything else from them:
host'video'forHTMLVideoAdapterand everything on it,'audio'forHTMLAudioAdapterand the SPF audio adapters,'iframe'for Cloudflare, TikTok, Twitch, Vimeo, YouTube, Spotify. The hls.jsMuxAudioAdapteroverrides its video base to'audio'.defaultPropsstreamType, which they could always setFrom
defaultProps, each primitive default becomes a content attribute of that type, named by its WHATWG spelling or kebab-case (src,preload,stream-type), reset to the default when removed. Objects such assourcestay property-only. It's the rulecreateMediaComponentalready applies to React props, so both façades expose one surface from one declaration.A boolean that defaults to
truestays property-only, since an absent attribute cannot meantrue. Themutedattribute belongs todefaultMuted, as in HTML, and seeds themutedstate when it changes.A
<video>or<audio>host also accepts what its native element understands (videoContentAttributes/audioContentAttributes, declared beside the base adapters). Ones the adapter can set go through the adapter; the rest are copied onto the inner element. An<iframe>host accepts only what its adapter declares.CustomMediaElementis now a compositionCustomMediaElement(Adapter, { template? })composes pieces that stand on their own in@videojs/media/dom:renderHost,AdapterAttachment,forwardAdapter/reflectAttributes/bridgeEvent, andMediaChildren.createMediaElementwraps it inMediaAttachMixin.<background-video>and<hls-background-video>userenderHostandAdapterAttachmentinstead of hand-rolling their shadow root and teardown. The six iframe embeds share oneembedTemplateand read their initial attributes throughpropsFromAttributes, the samedefaultPropscoercion the element applies later, instead of six copies of the markup and the mapper.static propertiesis gone; the element's staticgetTemplateHTMLis renamedtemplate.Also fixed on the way: definition state was one flag and one map per factory call, so two subclasses with different attribute sets shared the first one's; the light-DOM target lookup could adopt a
<video>nested in slotted content.Shape
createMediaElement(Adapter, options?)template<iframe>from the element's initial attributesConverted elements
Every built-in media element except
<wistia-video>(the adapter is itself an element) now uses the factory, including the six iframe embeds throughtemplate.Docs builder
Resolves each element from what its define entry passes to
safeDefine, follows the adapter's statichostthrough the extends chain to the base adapters, reads standard attributes from the base adapters' native lists, and derives custom attributes fromdefaultProps. Fixtures cover both factories, an iframe-backed audio element, and a flavour reached through a nested barrel.Breaking
HlsJsVideoElement,SpotifyAudioElement, and so on, inmedia/<name>/element.ts, and declare their owntagName.@videojs/html/media/<name>keeps exporting the element class.CustomMediaElementtakes an adapter, not a tag; adapters must declarestatic hostandstatic defaultProps;static propertiesis removed.poster,playsinline, or picture-in-picture; embeds reflect only the attributes their adapter declares.Review
Reviewed by gpt-5.6-sol against the branch; its findings on true-by-default booleans,
defaultMutedownership, field-backed adapter props, target replay on slot change, event bridging,MediaChildrenattribute removal, and the exported surface are all addressed in the last commits, with tests for each.Validation
Build, typecheck, lint, workspace check,
@videojs/media,@videojs/html, adapter, SPF audio, cdn, and builder tests,astro check, regenerated media references with all 14 targets intact.🤖 Generated with Claude Code