refactor(core): airplay button (#1614)

This commit is contained in:
Santiago Puppo
2026-06-03 13:54:28 -07:00
committed by GitHub
parent 127443aaa2
commit 24a78d253a
40 changed files with 314 additions and 198 deletions
+10
View File
@@ -34,3 +34,13 @@ export {
} from './text-track';
export { serializeTimeRanges } from './time-ranges';
export type { CustomElement, CustomElementCallbacks } from './types';
export {
isWebKitAirPlayCapable,
supportsWebKitAirPlay,
type WebKitAirPlayMedia,
type WebKitDocument,
type WebKitFullscreenElement,
type WebKitPresentationMode,
type WebKitVideoElement,
type WebkitAvailabilityEvent,
} from './webkit';
@@ -0,0 +1,48 @@
import { afterEach, describe, expect, it } from 'vitest';
import { isWebKitAirPlayCapable, supportsWebKitAirPlay } from '../webkit';
// jsdom lacks WebKit's AirPlay APIs, so stub the global support flag per test.
function stubWebKit(present: boolean) {
const key = 'WebKitPlaybackTargetAvailabilityEvent';
if (present) {
(globalThis as unknown as Record<string, unknown>)[key] = class {};
} else {
delete (globalThis as unknown as Record<string, unknown>)[key];
}
}
describe('webkit', () => {
afterEach(() => stubWebKit(false));
describe('supportsWebKitAirPlay', () => {
it('returns true when the WebKit availability event is present', () => {
stubWebKit(true);
expect(supportsWebKitAirPlay()).toBe(true);
});
it('returns false when absent', () => {
stubWebKit(false);
expect(supportsWebKitAirPlay()).toBe(false);
});
});
describe('isWebKitAirPlayCapable', () => {
it('returns true when supported and the media exposes the AirPlay flag', () => {
stubWebKit(true);
const media = { webkitCurrentPlaybackTargetIsWireless: false } as unknown as EventTarget;
expect(isWebKitAirPlayCapable(media)).toBe(true);
});
it('returns false when WebKit is unsupported', () => {
stubWebKit(false);
const media = { webkitCurrentPlaybackTargetIsWireless: false } as unknown as EventTarget;
expect(isWebKitAirPlayCapable(media)).toBe(false);
});
it('returns false when the media lacks the AirPlay flag', () => {
stubWebKit(true);
expect(isWebKitAirPlayCapable(new EventTarget())).toBe(false);
});
});
});
+44
View File
@@ -0,0 +1,44 @@
/** WebKit-only addition to HTMLMediaElement exposing the active AirPlay flag. */
export interface WebKitAirPlayMedia extends HTMLMediaElement {
readonly webkitCurrentPlaybackTargetIsWireless: boolean;
}
/** WebKit presentation mode values for iOS Safari. */
export type WebKitPresentationMode = 'inline' | 'fullscreen' | 'picture-in-picture';
/** Extended HTMLVideoElement with WebKit vendor APIs. */
export interface WebKitVideoElement extends HTMLVideoElement {
/** Current WebKit presentation mode (iOS Safari). */
webkitPresentationMode?: WebKitPresentationMode;
/** Set WebKit presentation mode (iOS Safari). */
webkitSetPresentationMode?: (mode: WebKitPresentationMode) => void;
}
/** Extended Element with WebKit fullscreen vendor API. */
export interface WebKitFullscreenElement extends Element {
/** Request fullscreen using WebKit API (Safari). */
webkitRequestFullscreen?: () => Promise<void>;
}
/** Extended Document with WebKit fullscreen vendor APIs. */
export interface WebKitDocument extends Document {
/** Current fullscreen element (WebKit). */
webkitFullscreenElement?: Element | null;
/** Whether fullscreen is enabled (WebKit). */
webkitFullscreenEnabled?: boolean;
/** Exit fullscreen (WebKit). */
webkitExitFullscreen?: () => Promise<void>;
}
/** WebKit-specific AirPlay availability event payload (not in lib.dom). */
export type WebkitAvailabilityEvent = Event & { availability: 'available' | 'not-available' };
/** Whether WebKit's AirPlay APIs are present in this realm (Safari macOS/iOS). */
export function supportsWebKitAirPlay(): boolean {
return 'WebKitPlaybackTargetAvailabilityEvent' in globalThis;
}
/** Whether `media` exposes WebKit's AirPlay APIs. */
export function isWebKitAirPlayCapable(media: EventTarget): media is WebKitAirPlayMedia {
return supportsWebKitAirPlay() && 'webkitCurrentPlaybackTargetIsWireless' in media;
}