feat: use anchor API for html elements (#174)

This commit is contained in:
Wesley Luyten
2025-11-07 14:33:10 -06:00
committed by GitHub
parent 8d477a33a4
commit 4a2d580bb3
16 changed files with 191 additions and 215 deletions
+2 -2
View File
@@ -35,9 +35,9 @@
>
<video
slot="media"
src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4"
src="https://stream.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ/high.mp4"
playsinline
poster="https://image.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/thumbnail.webp"
poster="https://image.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ/thumbnail.webp"
></video>
<div class="overlay"></div>
+2 -2
View File
@@ -35,9 +35,9 @@
>
<video
slot="media"
src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4"
src="https://stream.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ/high.mp4"
playsinline
poster="https://image.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/thumbnail.webp"
poster="https://image.mux.com/fXNzVtmtWuyz00xnSrJg4OJH6PyNo6D02UzmgeKGkP5YQ/thumbnail.webp"
></video>
<div class="overlay"></div>
+1 -1
View File
@@ -1,6 +1,6 @@
import './frosted-eject.css';
import '@videojs/html/icons';
// be sure to import video-provider first
// be sure to import video-provider first for proper context initialization
import '@videojs/html/define/video-provider';
import '@videojs/html/define/media-container';
import '@videojs/html/define/media-play-button';
+1 -1
View File
@@ -1,6 +1,6 @@
import './minimal-eject.css';
import '@videojs/html/icons';
// be sure to import video-provider first
// be sure to import video-provider first for proper context initialization
import '@videojs/html/define/video-provider';
import '@videojs/html/define/media-container';
import '@videojs/html/define/media-play-button';
-2
View File
@@ -52,8 +52,6 @@
"clean": "rm -rf dist"
},
"dependencies": {
"@floating-ui/core": "^1.6.13",
"@floating-ui/dom": "^1.6.13",
"@open-wc/context-protocol": "^0.0.9",
"@videojs/core": "workspace:*",
"@videojs/icons": "workspace:*",
+2 -1
View File
@@ -1,5 +1,6 @@
import './media-container';
// be sure to import video-provider first for proper context initialization
import './video-provider';
import './media-container';
import './media-play-button';
import './media-mute-button';
import './media-volume-slider';
+8
View File
@@ -27,6 +27,8 @@ export class ButtonElement extends HTMLElement {
static getTemplateHTML: typeof getTemplateHTML = getTemplateHTML;
static observedAttributes: string[] = ['commandfor'];
constructor() {
super();
@@ -46,6 +48,12 @@ export class ButtonElement extends HTMLElement {
this.addEventListener('keydown', this);
}
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void {
if (name === 'commandfor') {
this.style.setProperty('anchor-name', `--${newValue}`);
}
}
handleEvent(event: Event): void {
const { type } = event;
if (type === 'keydown') {
+21 -54
View File
@@ -1,26 +1,27 @@
import type { ComputePositionReturn } from '@floating-ui/core';
import type { Placement } from '@floating-ui/dom';
import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
import { contains, getDocument, getDocumentOrShadowRoot, safePolygon } from '@videojs/utils/dom';
type Prettify<T> = {
[K in keyof T]: T[K];
};
type FloatingContext = Prettify<ComputePositionReturn> & {
elements: {
domReference: HTMLElement;
floating: HTMLElement;
};
};
type Placement = 'top' | 'bottom' | 'left' | 'right';
export class PopoverElement extends HTMLElement {
static get observedAttributes(): string[] {
return ['id', 'open-on-hover', 'delay', 'close-delay', 'side', 'side-offset'];
}
#open = false;
#transitionStatus: 'initial' | 'open' | 'close' | 'unmounted' = 'initial';
#hoverTimeout: ReturnType<typeof setTimeout> | null = null;
#cleanup: (() => void) | null = null;
#abortController: AbortController | null = null;
#floatingContext: FloatingContext | null = null;
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void {
if (name === 'id') {
this.style.setProperty('position-anchor', `--${newValue}`);
}
this.style.setProperty('top', `calc(anchor(${this.side}) - ${this.sideOffset}px)`);
this.style.setProperty('translate', `0 -100%`);
this.style.setProperty('justify-self', 'anchor-center');
}
connectedCallback(): void {
this.#abortController ??= new AbortController();
@@ -71,10 +72,6 @@ export class PopoverElement extends HTMLElement {
}
}
static get observedAttributes(): string[] {
return ['open-on-hover', 'delay', 'close-delay', 'side', 'side-offset'];
}
get openOnHover(): boolean {
return this.hasAttribute('open-on-hover');
}
@@ -105,8 +102,6 @@ export class PopoverElement extends HTMLElement {
this.#open = open;
if (open) {
this.#setupFloating();
this.#transitionStatus = 'initial';
this.#updateVisibility();
@@ -141,38 +136,6 @@ export class PopoverElement extends HTMLElement {
this.toggleAttribute('data-closed', this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted');
}
#setupFloating(): void {
const trigger = this.#triggerElement as HTMLElement;
if (!trigger) return;
const placement = this.side ?? 'top';
const sideOffset = this.sideOffset;
const updatePosition = () => {
computePosition(trigger, this, {
placement,
middleware: [offset(sideOffset), flip(), shift()],
strategy: 'fixed',
}).then((data: ComputePositionReturn) => {
this.#floatingContext = {
...data,
elements: {
domReference: trigger,
floating: this,
},
};
Object.assign(this.style, {
left: `${data.x}px`,
top: `${data.y}px`,
});
});
};
updatePosition();
this.#cleanup = autoUpdate(trigger, this, updatePosition);
}
#clearHoverTimeout(): void {
if (this.#hoverTimeout) {
globalThis.clearTimeout(this.#hoverTimeout);
@@ -210,10 +173,14 @@ export class PopoverElement extends HTMLElement {
}
#handlePointerMove(event: PointerEvent): void {
if (!this.openOnHover || !this.#floatingContext) return;
if (!this.openOnHover || !this.#triggerElement) return;
const close = safePolygon({ blockPointerEvents: true })({
...this.#floatingContext,
placement: this.side,
elements: {
domReference: this.#triggerElement,
floating: this,
},
x: event.clientX,
y: event.clientY,
onClose: () => {
+4 -2
View File
@@ -50,7 +50,7 @@ export const getTimeSliderRootProps: PropsHook<{
};
export class TimeSliderRoot extends HTMLElement {
static readonly observedAttributes: readonly string[] = ['orientation'];
static readonly observedAttributes: readonly string[] = ['commandfor', 'orientation'];
_state: TimeSliderRootState | undefined;
_core: CoreTimeSlider | null = null;
@@ -67,9 +67,11 @@ export class TimeSliderRoot extends HTMLElement {
return (this.getAttribute('orientation') as 'horizontal' | 'vertical') || 'horizontal';
}
attributeChangedCallback(name: string, _oldValue: string | null, _newValue: string | null): void {
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {
if (name === 'orientation' && this._state) {
this._render(getTimeSliderRootProps(this._state, this), this._state);
} else if (name === 'commandfor') {
this.style.setProperty('anchor-name', `--${newValue}`);
}
}
+63 -112
View File
@@ -1,18 +1,46 @@
import type { Placement } from '@floating-ui/dom';
import type { MediaContainerElement } from '@/media/media-container';
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
import { getDocumentOrShadowRoot } from '@videojs/utils/dom';
import {
getBoundingClientRectWithoutTransform,
getDocumentOrShadowRoot,
getInBoundsAdjustments,
} from '@videojs/utils/dom';
type Placement = 'top' | 'bottom' | 'left' | 'right';
export class TooltipElement extends HTMLElement {
static get observedAttributes(): string[] {
return ['id', 'delay', 'close-delay', 'track-cursor-axis', 'side', 'side-offset', 'collision-padding'];
}
#open = false;
#hoverTimeout: ReturnType<typeof setTimeout> | null = null;
#cleanup: (() => void) | null = null;
#arrowElement: HTMLElement | null = null;
#pointerPosition = { x: 0, y: 0 };
#transitionStatus: 'initial' | 'open' | 'close' | 'unmounted' = 'initial';
#abortController: AbortController | null = null;
constructor() {
super();
const resizeObserver = new ResizeObserver(() => this.#checkCollision());
resizeObserver.observe(this);
}
attributeChangedCallback(name: string, _oldValue: string, newValue: string): void {
if (name === 'id') {
this.style.setProperty('position-anchor', `--${newValue}`);
}
this.style.setProperty('top', `calc(anchor(${this.side}) - ${this.sideOffset}px)`);
if (this.trackCursorAxis) {
this.style.setProperty('translate', `-50% -100%`);
} else {
this.style.setProperty('translate', `0 -100%`);
this.style.setProperty('justify-self', 'anchor-center');
}
}
connectedCallback(): void {
this.setAttribute('role', 'tooltip');
@@ -33,7 +61,6 @@ export class TooltipElement extends HTMLElement {
disconnectedCallback(): void {
this.#clearHoverTimeout();
this.#cleanup?.();
this.#abortController?.abort();
this.#abortController = null;
@@ -57,10 +84,6 @@ export class TooltipElement extends HTMLElement {
}
}
static get observedAttributes(): string[] {
return ['delay', 'close-delay', 'track-cursor-axis', 'side', 'side-offset', 'collision-padding'];
}
get delay(): number {
return Number.parseInt(this.getAttribute('delay') ?? '0', 10);
}
@@ -96,8 +119,6 @@ export class TooltipElement extends HTMLElement {
this.#open = open;
if (open) {
this.#setupFloating();
this.#transitionStatus = 'initial';
this.#updateVisibility();
@@ -106,6 +127,7 @@ export class TooltipElement extends HTMLElement {
requestAnimationFrame(() => {
this.#transitionStatus = 'open';
this.#updateVisibility();
this.#checkCollision();
});
} else {
this.#transitionStatus = 'close';
@@ -119,16 +141,16 @@ export class TooltipElement extends HTMLElement {
} else {
this.hidePopover();
}
this.#cleanup?.();
this.#cleanup = null;
}
}
#updateVisibility(): void {
this.toggleAttribute('data-starting-style', this.#transitionStatus === 'initial');
this.toggleAttribute('data-open', this.#transitionStatus === 'initial' || this.#transitionStatus === 'open');
this.toggleAttribute('data-ending-style', this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted');
this.toggleAttribute(
'data-ending-style',
this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted',
);
this.toggleAttribute('data-closed', this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted');
const triggerElement = this.#triggerElement as HTMLElement;
@@ -137,105 +159,34 @@ export class TooltipElement extends HTMLElement {
}
}
#setupFloating(): void {
const trigger = this.#triggerElement as HTMLElement;
if (!trigger) return;
const placement = this.side;
const sideOffset = this.sideOffset;
const collisionPadding = this.collisionPadding;
const mediaContainer = this.closest('media-container') as MediaContainerElement;
this.#arrowElement = this.querySelector('media-tooltip-arrow') as HTMLElement;
const updatePosition = () => {
const middleware = [
offset(sideOffset),
flip(),
shift({
boundary: mediaContainer,
padding: collisionPadding,
}),
];
if (this.#arrowElement) {
middleware.push(arrow({ element: this.#arrowElement }));
}
const referenceElement = this.trackCursorAxis
? {
getBoundingClientRect: () => {
const triggerRect = trigger.getBoundingClientRect();
if (this.trackCursorAxis === 'x') {
return {
width: 0,
height: 0,
top: triggerRect.top,
right: this.#pointerPosition.x,
bottom: triggerRect.bottom,
left: this.#pointerPosition.x,
x: this.#pointerPosition.x,
y: triggerRect.top,
};
} else if (this.trackCursorAxis === 'y') {
return {
width: 0,
height: 0,
top: this.#pointerPosition.y,
right: triggerRect.right,
bottom: this.#pointerPosition.y,
left: triggerRect.left,
x: triggerRect.left,
y: this.#pointerPosition.y,
};
} else {
// Track both axes (trackCursorAxis === 'both')
return {
width: 0,
height: 0,
top: this.#pointerPosition.y,
right: this.#pointerPosition.x,
bottom: this.#pointerPosition.y,
left: this.#pointerPosition.x,
x: this.#pointerPosition.x,
y: this.#pointerPosition.y,
};
}
},
}
: trigger;
computePosition(referenceElement, this, {
placement,
middleware,
strategy: 'fixed',
}).then(({ x, y, middlewareData }: { x: number; y: number; middlewareData: any }) => {
Object.assign(this.style, {
left: `${x}px`,
top: `${y}px`,
});
if (this.#arrowElement && middlewareData.arrow) {
const { x: arrowX, y: arrowY } = middlewareData.arrow;
Object.assign(this.#arrowElement.style, {
left: arrowX != null ? `${arrowX}px` : undefined,
top: arrowY != null ? `${arrowY}px` : undefined,
});
}
});
};
updatePosition();
if (!this.trackCursorAxis) {
this.#cleanup = autoUpdate(trigger, this, updatePosition);
#updatePosition(): void {
if (this.#open && this.trackCursorAxis) {
this.style.setProperty('left', `${this.#pointerPosition.x}px`);
this.#checkCollision();
}
}
#updatePosition(): void {
if (this.#open && this.trackCursorAxis) {
this.#setupFloating();
#checkCollision(): void {
const mediaContainer = this.closest('media-container') as MediaContainerElement;
if (!mediaContainer || !this.#open) return;
const popupRect = getBoundingClientRectWithoutTransform(this);
const boundsRect = getBoundingClientRectWithoutTransform(mediaContainer);
const { x } = getInBoundsAdjustments(popupRect, boundsRect, this.collisionPadding);
if (x !== 0) {
if (this.trackCursorAxis) {
const currentLeft = Number.parseFloat(this.style.left || '0');
this.style.setProperty('left', `${currentLeft + x}px`);
} else {
this.style.setProperty('translate', `${x}px -100%`);
}
} else {
if (this.trackCursorAxis) {
this.style.setProperty('translate', '-50% -100%');
} else {
this.style.setProperty('translate', '0 -100%');
}
}
}
+5 -5
View File
@@ -132,7 +132,7 @@ export class VolumeSliderTrack extends HTMLElement {
/**
* VolumeSlider Progress component - Shows current progress
*/
export class VolumeSliderIndicatorElement extends HTMLElement {
export class VolumeSliderIndicator extends HTMLElement {
constructor() {
super();
this.style.position = 'absolute';
@@ -255,8 +255,8 @@ export const VolumeSliderTrackElement: ConnectedComponentConstructor<any> = toCo
/**
* Connected VolumeSlider Progress component
*/
export const VolumeSliderProgressElement: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
VolumeSliderIndicatorElement,
export const VolumeSliderIndicatorElement: ConnectedComponentConstructor<any> = toConnectedHTMLComponent(
VolumeSliderIndicator,
{ keys: [], transform: () => ({}) },
getVolumeSliderProgressProps,
'VolumeSliderProgress',
@@ -280,12 +280,12 @@ export const VolumeSliderElement = Object.assign(
{
Root: VolumeSliderRootElement,
Track: VolumeSliderTrackElement,
Progress: VolumeSliderProgressElement,
Indicator: VolumeSliderIndicatorElement,
Thumb: VolumeSliderThumbElement,
},
) as {
Root: typeof VolumeSliderRootElement;
Track: typeof VolumeSliderTrackElement;
Progress: typeof VolumeSliderProgressElement;
Indicator: typeof VolumeSliderIndicatorElement;
Thumb: typeof VolumeSliderThumbElement;
};
+2 -1
View File
@@ -1,8 +1,9 @@
import { MediaSkinElement } from '@/media/media-skin';
import { defineCustomElement } from '@/utils/custom-element';
import styles from './styles.css';
import '@/define/media-container';
// be sure to import video-provider first for proper context initialization
import '@/define/video-provider';
import '@/define/media-container';
import '@/define/media-play-button';
import '@/define/media-mute-button';
import '@/define/media-volume-slider';
+2 -1
View File
@@ -1,8 +1,9 @@
import { MediaSkinElement } from '@/media/media-skin';
import { defineCustomElement } from '@/utils/custom-element';
import styles from './styles.css';
import '@/define/media-container';
// be sure to import video-provider first for proper context initialization
import '@/define/video-provider';
import '@/define/media-container';
import '@/define/media-play-button';
import '@/define/media-mute-button';
import '@/define/media-volume-slider';
+59 -17
View File
@@ -3,9 +3,7 @@
*
* @param root - The root node to search for the active element.
*/
export function activeElement(
root: Document = document,
): Element | null {
export function activeElement(root: Document = document): Element | null {
let element = root.activeElement;
while (element?.shadowRoot?.activeElement != null) {
@@ -20,9 +18,7 @@ export function activeElement(
* https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode#return_value
* @param node - The node to get the root node from.
*/
export function getDocumentOrShadowRoot(
node: Node,
): Document | ShadowRoot | null {
export function getDocumentOrShadowRoot(node: Node): Document | ShadowRoot | null {
const rootNode = node?.getRootNode?.();
if (rootNode instanceof ShadowRoot || rootNode instanceof Document) {
return rootNode;
@@ -85,9 +81,7 @@ export function isShadowRoot(value: unknown): value is ShadowRoot {
return false;
}
return (
value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot
);
return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
}
function hasWindow() {
@@ -102,7 +96,7 @@ export interface FloatingNodeType {
id: string;
parentId: string | null;
context: FloatingContext;
};
}
interface FloatingContext {
open: boolean;
@@ -113,11 +107,59 @@ export function getNodeChildren(
id: string | undefined,
onlyOpenChildren = true,
): Array<FloatingNodeType> {
const directChildren = nodes.filter(
node => node.parentId === id && (!onlyOpenChildren || node.context?.open),
);
return directChildren.flatMap(child => [
child,
...getNodeChildren(nodes, child.id, onlyOpenChildren),
]);
const directChildren = nodes.filter(node => node.parentId === id && (!onlyOpenChildren || node.context?.open));
return directChildren.flatMap(child => [child, ...getNodeChildren(nodes, child.id, onlyOpenChildren)]);
}
export function getBoundingClientRectWithoutTransform(element: HTMLElement): DOMRect {
let el = element;
let left = 0;
let top = 0;
do {
left += el.offsetLeft;
top += el.offsetTop;
el = el.offsetParent as HTMLElement;
} while (el);
return {
x: left,
y: top,
left,
top,
bottom: top + element.offsetHeight,
right: left + element.offsetWidth,
width: element.offsetWidth,
height: element.offsetHeight,
} as DOMRect;
}
export function getInBoundsAdjustments(
popupRect: DOMRect,
containerRect: DOMRect,
collisionPadding: number,
): { x: number; y: number } {
const bounds = {
top: containerRect.top + collisionPadding,
right: containerRect.right - collisionPadding,
bottom: containerRect.bottom - collisionPadding,
left: containerRect.left + collisionPadding,
};
let x = 0;
let y = 0;
if (popupRect.left < bounds.left) {
x = bounds.left - popupRect.left;
} else if (popupRect.right > bounds.right) {
x = bounds.right - popupRect.right;
}
if (popupRect.top < bounds.top) {
y = bounds.top - popupRect.top;
} else if (popupRect.bottom > bounds.bottom) {
y = bounds.bottom - popupRect.bottom;
}
return { x, y };
}
+18 -13
View File
@@ -198,12 +198,6 @@ importers:
packages/html:
dependencies:
'@floating-ui/core':
specifier: ^1.6.13
version: 1.7.3
'@floating-ui/dom':
specifier: ^1.6.13
version: 1.7.4
'@open-wc/context-protocol':
specifier: ^0.0.9
version: 0.0.9
@@ -237,7 +231,7 @@ importers:
dependencies:
'@floating-ui/react':
specifier: ^0.27.16
version: 0.27.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 0.27.16(react-dom@19.2.0(react@18.3.1))(react@18.3.1)
'@videojs/core':
specifier: workspace:*
version: link:../core
@@ -7411,12 +7405,18 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@floating-ui/react@0.27.16(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
'@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@floating-ui/dom': 1.7.4
react: 18.3.1
react-dom: 19.2.0(react@18.3.1)
'@floating-ui/react@0.27.16(react-dom@19.2.0(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@18.3.1))(react@18.3.1)
'@floating-ui/utils': 0.2.10
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-dom: 19.2.0(react@18.3.1)
tabbable: 6.2.0
'@floating-ui/utils@0.2.10': {}
@@ -9775,7 +9775,7 @@ snapshots:
eslint: 9.37.0(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-react: 7.37.5(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-react-hooks: 7.0.1(eslint@9.37.0(jiti@2.6.1))
@@ -9817,7 +9817,7 @@ snapshots:
tinyglobby: 0.2.15
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))
transitivePeerDependencies:
- supports-color
@@ -9893,7 +9893,7 @@ snapshots:
optionalDependencies:
typescript: 5.9.2
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -12392,6 +12392,11 @@ snapshots:
react: 18.3.1
scheduler: 0.23.2
react-dom@19.2.0(react@18.3.1):
dependencies:
react: 18.3.1
scheduler: 0.27.0
react-dom@19.2.0(react@19.2.0):
dependencies:
react: 19.2.0
+1 -1
View File
@@ -1916,7 +1916,7 @@ export function generateHTMLJS(skin: Skin): string {
// npm install @videojs/html@next
import '@videojs/html/icons';
// be sure to import video-provider first
// be sure to import video-provider first for proper context initialization
import '@videojs/html/define/video-provider';
import '@videojs/html/define/media-container';
import '@videojs/html/define/media-play-button';