mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
fix: attaching media like elements and upgrade (#889)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -26,7 +26,7 @@
|
||||
"dev:site": "turbo run dev --filter=site",
|
||||
"dev": "turbo run dev --parallel",
|
||||
"dev:packages": "turbo run dev --parallel --filter='./packages/*'",
|
||||
"dev:sandbox": "turbo run dev --filter=@videojs/sandbox",
|
||||
"dev:sandbox": "turbo watch dev --filter=@videojs/sandbox",
|
||||
"lint": "biome check .",
|
||||
"lint:fix": "biome check . --write",
|
||||
"lint:fix:file": "biome check --write",
|
||||
|
||||
@@ -434,9 +434,6 @@ export function CustomMediaMixin<T extends Constructor<HTMLElement>>(
|
||||
|
||||
connectedCallback(): void {
|
||||
this.#init();
|
||||
// Set after #init() so the shadow root exists; safe here since
|
||||
// connectedCallback is spec-compliant for setAttribute (unlike constructor).
|
||||
this.setAttribute('data-media-element', '');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,14 +46,14 @@ export function createContainerMixin<Store extends PlayerStore>(context: PlayerC
|
||||
super.connectedCallback();
|
||||
|
||||
this.#observer = new MutationObserver((records) => {
|
||||
if (records.some(hasMediaNode)) this.#attachMedia();
|
||||
if (records.some(hasMediaElement)) this.#attachMedia();
|
||||
});
|
||||
|
||||
this.#observer.observe(this, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['data-media-element'],
|
||||
attributeFilter: ['name'],
|
||||
});
|
||||
|
||||
// Slotted media elements don't appear in the container's subtree,
|
||||
@@ -80,12 +80,18 @@ export function createContainerMixin<Store extends PlayerStore>(context: PlayerC
|
||||
if (!slot) return null;
|
||||
|
||||
for (const el of slot.assignedElements({ flatten: true })) {
|
||||
if (el instanceof HTMLMediaElement) return el;
|
||||
if (isMediaElement(el)) return el as HTMLMediaElement;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#findMediaElement(): HTMLMediaElement | null {
|
||||
const media = Array.from(this.children).find(isMediaElement);
|
||||
if (media) return media as HTMLMediaElement;
|
||||
return null;
|
||||
}
|
||||
|
||||
#attachMedia() {
|
||||
// Prefer the cached context value; fall back to `this.store` which
|
||||
// ProviderMixin overrides when both mixins are applied to one element.
|
||||
@@ -93,7 +99,7 @@ export function createContainerMixin<Store extends PlayerStore>(context: PlayerC
|
||||
if (!store) return;
|
||||
|
||||
const media =
|
||||
this.querySelector<HTMLMediaElement>('video, audio, [data-media-element]') ?? this.#getSlottedMedia();
|
||||
this.querySelector<HTMLMediaElement>('video, audio') ?? this.#findMediaElement() ?? this.#getSlottedMedia();
|
||||
|
||||
if (!media) {
|
||||
this.#detach();
|
||||
@@ -101,6 +107,10 @@ export function createContainerMixin<Store extends PlayerStore>(context: PlayerC
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCustomMediaElement(media)) {
|
||||
globalThis.customElements?.upgrade?.(media);
|
||||
}
|
||||
|
||||
const target: PlayerTarget = {
|
||||
media,
|
||||
container: this,
|
||||
@@ -120,22 +130,27 @@ export function createContainerMixin<Store extends PlayerStore>(context: PlayerC
|
||||
};
|
||||
}
|
||||
|
||||
function isMediaNode(node: Node): boolean {
|
||||
return node instanceof HTMLMediaElement || (node instanceof Element && node.hasAttribute('data-media-element'));
|
||||
function isMediaElement(node: Node): boolean {
|
||||
return node instanceof HTMLMediaElement || isCustomMediaElement(node);
|
||||
}
|
||||
|
||||
function hasMediaNode(record: MutationRecord): boolean {
|
||||
// Attribute mutation: data-media-element was added to a descendant
|
||||
if (record.type === 'attributes' && record.target instanceof Element) {
|
||||
return record.target.hasAttribute('data-media-element');
|
||||
}
|
||||
function isCustomMediaElement(node: Node): boolean {
|
||||
return node instanceof HTMLElement && (node.localName.endsWith('-audio') || node.localName.endsWith('-video'));
|
||||
}
|
||||
|
||||
function isMediaSlotElement(node: Node): boolean {
|
||||
return node instanceof HTMLSlotElement && node.name === 'media';
|
||||
}
|
||||
|
||||
function hasMediaElement(record: MutationRecord): boolean {
|
||||
if (isMediaSlotElement(record.target)) return true;
|
||||
|
||||
for (const node of record.addedNodes) {
|
||||
if (isMediaNode(node)) return true;
|
||||
if (isMediaElement(node) || isMediaSlotElement(node)) return true;
|
||||
}
|
||||
|
||||
for (const node of record.removedNodes) {
|
||||
if (isMediaNode(node)) return true;
|
||||
if (isMediaElement(node) || isMediaSlotElement(node)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user