diff --git a/examples/html-demo/frosted-eject.html b/examples/html-demo/frosted-eject.html
index 6fc81660..b24883a4 100644
--- a/examples/html-demo/frosted-eject.html
+++ b/examples/html-demo/frosted-eject.html
@@ -35,9 +35,9 @@
>
diff --git a/examples/html-demo/minimal-eject.html b/examples/html-demo/minimal-eject.html
index d894a924..f72ac378 100644
--- a/examples/html-demo/minimal-eject.html
+++ b/examples/html-demo/minimal-eject.html
@@ -35,9 +35,9 @@
>
diff --git a/examples/html-demo/src/frosted-eject.ts b/examples/html-demo/src/frosted-eject.ts
index 8d60c4d8..6c4f1ad6 100644
--- a/examples/html-demo/src/frosted-eject.ts
+++ b/examples/html-demo/src/frosted-eject.ts
@@ -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';
diff --git a/examples/html-demo/src/minimal-eject.ts b/examples/html-demo/src/minimal-eject.ts
index a0d011e5..1dbbdc1c 100644
--- a/examples/html-demo/src/minimal-eject.ts
+++ b/examples/html-demo/src/minimal-eject.ts
@@ -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';
diff --git a/packages/html/package.json b/packages/html/package.json
index f83f5347..a9c5fc18 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -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:*",
diff --git a/packages/html/src/define/index.ts b/packages/html/src/define/index.ts
index af271e41..239d0bed 100644
--- a/packages/html/src/define/index.ts
+++ b/packages/html/src/define/index.ts
@@ -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';
diff --git a/packages/html/src/elements/button.ts b/packages/html/src/elements/button.ts
index d2d1521b..be72c1ba 100644
--- a/packages/html/src/elements/button.ts
+++ b/packages/html/src/elements/button.ts
@@ -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') {
diff --git a/packages/html/src/elements/popover.ts b/packages/html/src/elements/popover.ts
index c47c82b1..ccb848c3 100644
--- a/packages/html/src/elements/popover.ts
+++ b/packages/html/src/elements/popover.ts
@@ -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 = {
- [K in keyof T]: T[K];
-};
-
-type FloatingContext = Prettify & {
- 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 | 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: () => {
diff --git a/packages/html/src/elements/time-slider.ts b/packages/html/src/elements/time-slider.ts
index 3fe0f3f8..44cf6b14 100644
--- a/packages/html/src/elements/time-slider.ts
+++ b/packages/html/src/elements/time-slider.ts
@@ -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}`);
}
}
diff --git a/packages/html/src/elements/tooltip.ts b/packages/html/src/elements/tooltip.ts
index a79965f6..2bafbf30 100644
--- a/packages/html/src/elements/tooltip.ts
+++ b/packages/html/src/elements/tooltip.ts
@@ -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 | 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%');
+ }
}
}
diff --git a/packages/html/src/elements/volume-slider.ts b/packages/html/src/elements/volume-slider.ts
index 9b71b997..f5f87e36 100644
--- a/packages/html/src/elements/volume-slider.ts
+++ b/packages/html/src/elements/volume-slider.ts
@@ -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 = toCo
/**
* Connected VolumeSlider Progress component
*/
-export const VolumeSliderProgressElement: ConnectedComponentConstructor = toConnectedHTMLComponent(
- VolumeSliderIndicatorElement,
+export const VolumeSliderIndicatorElement: ConnectedComponentConstructor = 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;
};
diff --git a/packages/html/src/skins/frosted/index.ts b/packages/html/src/skins/frosted/index.ts
index daea0162..0ccf2160 100644
--- a/packages/html/src/skins/frosted/index.ts
+++ b/packages/html/src/skins/frosted/index.ts
@@ -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';
diff --git a/packages/html/src/skins/minimal/index.ts b/packages/html/src/skins/minimal/index.ts
index 2d5b7de9..9ec14ce1 100644
--- a/packages/html/src/skins/minimal/index.ts
+++ b/packages/html/src/skins/minimal/index.ts
@@ -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';
diff --git a/packages/utils/src/dom/element.ts b/packages/utils/src/dom/element.ts
index 7ab1e2bf..5c7f215f 100644
--- a/packages/utils/src/dom/element.ts
+++ b/packages/utils/src/dom/element.ts
@@ -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 {
- 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 };
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 33916796..5113f682 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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
diff --git a/site/src/utils/ejectCodeGenerator.ts b/site/src/utils/ejectCodeGenerator.ts
index a2a4948f..7ecadf52 100644
--- a/site/src/utils/ejectCodeGenerator.ts
+++ b/site/src/utils/ejectCodeGenerator.ts
@@ -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';