diff --git a/packages/html/package.json b/packages/html/package.json
index 8708239f..1dc9e483 100644
--- a/packages/html/package.json
+++ b/packages/html/package.json
@@ -48,6 +48,7 @@
"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:*",
diff --git a/packages/html/src/components/media-popover.ts b/packages/html/src/components/media-popover.ts
index eafee19a..43a5884f 100644
--- a/packages/html/src/components/media-popover.ts
+++ b/packages/html/src/components/media-popover.ts
@@ -1,48 +1,64 @@
+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 { uniqueId } from '@videojs/utils';
+import { contains, getDocument, getDocumentOrShadowRoot, safePolygon } from '@videojs/utils/dom';
-import { getDocument, getNextTabbable, getPreviousTabbable, isOutsideEvent } from '@videojs/utils/dom';
+type Prettify = {
+ [K in keyof T]: T[K];
+};
-export class MediaPopoverRoot extends HTMLElement {
+type FloatingContext = Prettify & {
+ elements: {
+ domReference: HTMLElement;
+ floating: HTMLElement;
+ };
+};
+
+class Popover extends HTMLElement {
#open = false;
+ #transitionStatus: 'initial' | 'open' | 'close' | 'unmounted' = 'initial';
#hoverTimeout: ReturnType | null = null;
#cleanup: (() => void) | null = null;
- #transitionStatus: 'initial' | 'open' | 'close' | 'unmounted' = 'initial';
#abortController: AbortController | null = null;
+ #floatingContext: FloatingContext | null = null;
connectedCallback(): void {
- this.#updateVisibility();
-
this.#abortController ??= new AbortController();
const { signal } = this.#abortController;
- this.addEventListener('mouseenter', this, { signal });
- this.addEventListener('mouseleave', this, { signal });
- this.addEventListener('focusin', this, { signal });
- this.addEventListener('focusout', this, { signal });
+ const trigger = this.#triggerElement as HTMLElement;
+ if (trigger) {
+ if (globalThis.matchMedia?.('(hover: hover)')?.matches) {
+ trigger.addEventListener('pointerenter', this, { signal });
+ trigger.addEventListener('pointerleave', this, { signal });
+ }
- getDocument(this).documentElement.addEventListener('mouseleave', this, { signal });
+ trigger.addEventListener('focusin', this, { signal });
+ trigger.addEventListener('focusout', this, { signal });
+ }
+
+ this.addEventListener('pointerenter', this, { signal });
+ this.addEventListener('focusout', this, { signal });
}
disconnectedCallback(): void {
this.#clearHoverTimeout();
this.#cleanup?.();
- this.#transitionStatus = 'unmounted';
- this.#updateVisibility();
-
this.#abortController?.abort();
this.#abortController = null;
}
handleEvent(event: Event): void {
switch (event.type) {
- case 'mouseenter':
- this.#handleMouseEnter();
+ case 'pointerenter':
+ this.#handlePointerEnter(event as PointerEvent);
break;
- case 'mouseleave':
- this.#handleMouseLeave(event as MouseEvent);
+ case 'pointerleave':
+ this.#handlePointerLeave(event as PointerEvent);
+ break;
+ case 'pointermove':
+ this.#handlePointerMove(event as PointerEvent);
break;
case 'focusin':
this.#handleFocusIn(event as FocusEvent);
@@ -56,7 +72,7 @@ export class MediaPopoverRoot extends HTMLElement {
}
static get observedAttributes(): string[] {
- return ['open-on-hover', 'delay', 'close-delay'];
+ return ['open-on-hover', 'delay', 'close-delay', 'side', 'side-offset'];
}
get openOnHover(): boolean {
@@ -71,339 +87,6 @@ export class MediaPopoverRoot extends HTMLElement {
return Number.parseInt(this.getAttribute('close-delay') ?? '0', 10);
}
- get #triggerElement(): MediaPopoverTrigger | null {
- return this.querySelector('media-popover-trigger') as MediaPopoverTrigger | null;
- }
-
- get #portalElement(): MediaPopoverPortal | null {
- return this.querySelector('media-popover-portal') as MediaPopoverPortal | null;
- }
-
- get #positionerElement(): MediaPopoverPositioner | null {
- return this.#portalElement?.querySelector('media-popover-positioner') as MediaPopoverPositioner | null;
- }
-
- get #popupElement(): MediaPopoverPopup | null {
- return this.#portalElement?.querySelector('media-popover-popup') as MediaPopoverPopup | null;
- }
-
- setOpen(open: boolean): void {
- if (this.#open === open) return;
-
- this.#open = open;
-
- if (open) {
- this.#setupFloating();
- this.#portalElement?.renderGuards();
- } else {
- this.#portalElement?.removeGuards();
- this.#cleanup?.();
- this.#cleanup = null;
- }
-
- if (open) {
- this.#transitionStatus = 'initial';
- requestAnimationFrame(() => {
- this.#transitionStatus = 'open';
- this.#updateVisibility();
- });
- } else {
- this.#transitionStatus = 'close';
- }
-
- this.#updateVisibility();
- }
-
- #updateVisibility(): void {
- this.style.display = 'contents';
-
- if (this.#popupElement) {
- const placement = this.#positionerElement?.side ?? 'top';
- this.#popupElement.setAttribute('data-side', placement);
-
- this.#popupElement.toggleAttribute('data-starting-style', this.#transitionStatus === 'initial');
- this.#popupElement.toggleAttribute('data-open', this.#transitionStatus === 'initial' || this.#transitionStatus === 'open');
- this.#popupElement.toggleAttribute('data-ending-style', this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted');
- this.#popupElement.toggleAttribute('data-closed', this.#transitionStatus === 'close' || this.#transitionStatus === 'unmounted');
-
- this.#abortController ??= new AbortController();
- const { signal } = this.#abortController;
- this.#popupElement.addEventListener('mouseleave', this, { signal });
- }
-
- const triggerElement = this.#triggerElement?.firstElementChild as HTMLElement;
- if (triggerElement) {
- triggerElement.setAttribute('aria-expanded', this.#open.toString());
- triggerElement.toggleAttribute('data-popup-open', this.#open);
-
- if (this.#popupElement?.id) {
- triggerElement.setAttribute('aria-controls', this.#popupElement?.id);
- }
- }
- }
-
- #setupFloating(): void {
- if (!this.#triggerElement || !this.#popupElement) return;
-
- const trigger = this.#triggerElement.firstElementChild as HTMLElement;
- const popup = this.#popupElement;
-
- if (!trigger || !popup) return;
-
- const placement = this.#positionerElement?.side ?? 'top';
- const sideOffset = this.#positionerElement?.sideOffset;
-
- const updatePosition = () => {
- computePosition(trigger, popup, {
- placement,
- middleware: [offset(sideOffset), flip(), shift()],
- }).then(({ x, y }: { x: number; y: number }) => {
- Object.assign(popup.style, {
- left: `${x}px`,
- top: `${y}px`,
- });
- });
- };
-
- updatePosition();
- this.#cleanup = autoUpdate(trigger, popup, updatePosition);
- }
-
- #clearHoverTimeout(): void {
- if (this.#hoverTimeout) {
- clearTimeout(this.#hoverTimeout);
- this.#hoverTimeout = null;
- }
- }
-
- #handleMouseEnter(): void {
- if (!this.openOnHover) return;
-
- this.#clearHoverTimeout();
- this.#hoverTimeout = globalThis.setTimeout(() => {
- this.setOpen(true);
- }, this.delay);
- }
-
- #handleMouseLeave(event: MouseEvent): void {
- if (!this.openOnHover) return;
-
- if (event.relatedTarget && this.#popupElement?.contains(event.relatedTarget as Node)) return;
-
- this.#clearHoverTimeout();
- this.#hoverTimeout = globalThis.setTimeout(() => {
- this.setOpen(false);
- }, this.closeDelay);
- }
-
- #handleFocusIn(_event: FocusEvent): void {
- this.setOpen(true);
- }
-
- #handleFocusOut(event: FocusEvent): void {
- const relatedTarget = event.relatedTarget as HTMLElement;
- if (relatedTarget && relatedTarget.hasAttribute('data-focus-guard')) return;
-
- this.setOpen(false);
- };
-}
-
-export class MediaPopoverTrigger extends HTMLElement {
- connectedCallback(): void {
- this.style.display = 'contents';
-
- const triggerElement = this.firstElementChild as HTMLElement;
- if (triggerElement) {
- triggerElement.setAttribute('aria-haspopup', 'true');
- triggerElement.setAttribute('aria-expanded', 'false');
-
- const mutationObserver = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.type === 'attributes') {
- const rootElement = this.closest('media-popover-root') as MediaPopoverRoot;
- let popupElement = rootElement.querySelector('media-popover-popup') as MediaPopoverPopup;
-
- if (!popupElement) {
- const portalElement = rootElement.querySelector('media-popover-portal') as MediaPopoverPortal;
- if (!portalElement) {
- return;
- }
-
- popupElement = portalElement.querySelector('media-popover-popup') as MediaPopoverPopup;
- if (!popupElement) {
- return;
- }
- }
-
- const attributeName = mutation.attributeName;
- if (!attributeName || !attributeName.startsWith('data-')) {
- return;
- }
-
- const attributeValue = triggerElement.getAttribute(attributeName);
- if (attributeValue !== null) {
- popupElement.setAttribute(attributeName, attributeValue);
- } else {
- popupElement.removeAttribute(attributeName);
- }
- }
- });
- });
-
- mutationObserver.observe(triggerElement, {
- attributes: true,
- });
- }
- }
-}
-
-export class MediaPopoverPortal extends HTMLElement {
- #portal: HTMLElement | null = null;
- #childrenArray: Element[] = [];
- #guards: HTMLElement[] = [];
-
- connectedCallback(): void {
- this.style.display = 'contents';
- this.#setupPortal();
- }
-
- disconnectedCallback(): void {
- this.#cleanupPortal();
- }
-
- querySelector(selector: string): HTMLElement | null {
- return this.#portal!.querySelector(selector);
- }
-
- querySelectorAll(selector: string): NodeListOf {
- return this.#portal!.querySelectorAll(selector);
- }
-
- handleEvent(event: Event): void {
- this.dispatchEvent(new Event(event.type, { bubbles: true }));
- }
-
- #setupPortal(): void {
- const portalId = this.getAttribute('root-id') ?? '@default_portal_id';
- if (!portalId) return;
-
- /* @TODO We need to make sure portal logic is non-brittle longer term (CJP) */
- // NOTE: Hacky solution in part to ensure styling propagates from skin to container's baked in portal (TL;DR - Shadow DOM vs. Light DOM CSS) (CJP)
- const portalContainer
- = ((this.getRootNode() as ShadowRoot | Document).getElementById(portalId)
- ?? (this.getRootNode() as ShadowRoot | Document)
- .querySelector('media-container')
- ?.shadowRoot
- ?.getElementById(portalId))
- ? (this.getRootNode() as ShadowRoot | Document).querySelector('media-container')
- : undefined;
- if (!portalContainer) return;
-
- this.#portal = document.createElement('div');
- this.#portal.slot = 'portal';
- this.#portal.id = uniqueId();
-
- this.#childrenArray = Array.from(this.children);
- this.#portal.append(...this.#childrenArray);
- portalContainer.append(this.#portal);
- }
-
- #cleanupPortal(): void {
- if (!this.#portal) return;
-
- this.removeGuards();
-
- this.append(...this.#childrenArray);
- this.#portal.remove();
- this.#portal = null;
- this.#childrenArray = [];
- }
-
- renderGuards(): void {
- if (!this.#portal) return;
-
- if (this.#guards.length === 0) {
- const beforeInsideGuard = createFocusGuard('inside');
- const afterInsideGuard = createFocusGuard('inside');
- const beforeOutsideGuard = createFocusGuard('outside');
- const afterOutsideGuard = createFocusGuard('outside');
-
- beforeOutsideGuard.addEventListener('focus', (event: FocusEvent) => {
- if (this.#portal && isOutsideEvent(event, this.#portal)) {
- beforeInsideGuard.focus();
- } else {
- getPreviousTabbable(this)?.focus();
- }
- });
-
- afterOutsideGuard.addEventListener('focus', (event: FocusEvent) => {
- if (this.#portal && isOutsideEvent(event, this.#portal)) {
- afterInsideGuard.focus();
- } else {
- getNextTabbable(this)?.focus();
- }
- });
-
- beforeInsideGuard.addEventListener('focus', (event: FocusEvent) => {
- if (this.#portal && isOutsideEvent(event, this.#portal)) {
- getNextTabbable(this.#portal)?.focus();
- } else {
- beforeOutsideGuard.focus();
- }
- });
-
- afterInsideGuard.addEventListener('focus', (event: FocusEvent) => {
- if (this.#portal && isOutsideEvent(event, this.#portal)) {
- getPreviousTabbable(this.#portal)?.focus();
- } else {
- afterOutsideGuard.focus();
- }
- });
-
- // Add guards to portal element (outside guards)
- this.prepend(beforeOutsideGuard);
- this.append(afterOutsideGuard);
-
- // Add guards to portal container (inside guards)
- this.#portal.prepend(beforeInsideGuard);
- this.#portal.append(afterInsideGuard);
-
- this.#guards = [beforeOutsideGuard, afterOutsideGuard, beforeInsideGuard, afterInsideGuard];
- }
- }
-
- removeGuards(): void {
- this.#guards.forEach(guard => guard.remove());
- this.#guards = [];
- }
-}
-
-const visuallyHiddenStyles = 'position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;';
-
-function createFocusGuard(dataType: 'inside' | 'outside'): HTMLElement {
- const focusGuard = document.createElement('span');
- focusGuard.setAttribute('data-type', dataType);
- focusGuard.setAttribute('tabindex', '0');
- focusGuard.setAttribute('data-focus-guard', '');
- focusGuard.setAttribute('aria-hidden', 'true');
- focusGuard.style.cssText = visuallyHiddenStyles;
- return focusGuard;
-}
-
-export class MediaPopoverPositioner extends HTMLElement {
- connectedCallback(): void {
- this.style.display = 'contents';
-
- const popup = this.firstElementChild as HTMLElement;
- if (popup) {
- Object.assign(popup.style, {
- position: 'absolute',
- top: '0',
- left: '0',
- });
- }
- }
-
get side(): Placement {
return this.getAttribute('side') as Placement;
}
@@ -411,48 +94,153 @@ export class MediaPopoverPositioner extends HTMLElement {
get sideOffset(): number {
return Number.parseInt(this.getAttribute('side-offset') ?? '0', 10);
}
-}
-export class MediaPopoverPopup extends HTMLElement {
- connectedCallback(): void {
- this.setAttribute('role', 'dialog');
- this.setAttribute('aria-modal', 'false');
- this.id = uniqueId();
+ get #triggerElement(): HTMLElement | null {
+ return getDocumentOrShadowRoot(this)?.querySelector(`[popovertarget="${this.id}"]`) as HTMLElement | null;
}
+
+ #setOpen(open: boolean): void {
+ if (this.#open === open) return;
+
+ this.#open = open;
+
+ if (open) {
+ this.#setupFloating();
+
+ this.#transitionStatus = 'initial';
+ this.#updateVisibility();
+
+ this.showPopover();
+
+ requestAnimationFrame(() => {
+ this.#transitionStatus = 'open';
+ this.#updateVisibility();
+ });
+ } else {
+ this.#transitionStatus = 'close';
+ this.#updateVisibility();
+
+ const transitions = this.getAnimations().filter(anim => anim instanceof CSSTransition);
+ if (transitions.length > 0) {
+ Promise.all(transitions.map(t => t.finished))
+ .then(() => this.hidePopover())
+ .catch(() => this.hidePopover());
+ } else {
+ this.hidePopover();
+ }
+ }
+ }
+
+ #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-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);
+ this.#hoverTimeout = null;
+ }
+ }
+
+ #handlePointerEnter(event: PointerEvent): void {
+ if (!this.openOnHover) return;
+
+ this.#clearHoverTimeout();
+
+ if (event.currentTarget === this) {
+ this.#addPointerMoveListener();
+ }
+
+ if (this.#open) {
+ return;
+ }
+
+ this.#hoverTimeout = globalThis.setTimeout(() => {
+ this.#setOpen(true);
+ }, this.delay);
+ }
+
+ #handlePointerLeave(_event: PointerEvent): void {
+ this.#addPointerMoveListener();
+ }
+
+ #addPointerMoveListener(): void {
+ if (!globalThis.matchMedia?.('(hover: hover)')?.matches) return;
+
+ const { signal } = this.#abortController as AbortController;
+ getDocument(this).documentElement.addEventListener('pointermove', this, { signal });
+ }
+
+ #handlePointerMove(event: PointerEvent): void {
+ if (!this.openOnHover || !this.#floatingContext) return;
+
+ const close = safePolygon({ blockPointerEvents: true })({
+ ...this.#floatingContext,
+ x: event.clientX,
+ y: event.clientY,
+ onClose: () => {
+ getDocument(this).documentElement.removeEventListener('pointermove', this);
+
+ this.#clearHoverTimeout();
+ this.#hoverTimeout = globalThis.setTimeout(() => {
+ this.#setOpen(false);
+ }, this.closeDelay);
+ },
+ });
+ close(event);
+ }
+
+ #handleFocusIn(_event: FocusEvent): void {
+ this.#setOpen(true);
+ }
+
+ #handleFocusOut(event: FocusEvent): void {
+ const relatedTarget = event.relatedTarget as HTMLElement;
+ if (relatedTarget && contains(this, relatedTarget)) return;
+
+ this.#setOpen(false);
+ };
}
-if (!globalThis.customElements.get('media-popover-root')) {
- globalThis.customElements.define('media-popover-root', MediaPopoverRoot);
+if (!globalThis.customElements.get('media-popover')) {
+ globalThis.customElements.define('media-popover', Popover);
}
-if (!globalThis.customElements.get('media-popover-trigger')) {
- globalThis.customElements.define('media-popover-trigger', MediaPopoverTrigger);
-}
-
-if (!globalThis.customElements.get('media-popover-portal')) {
- globalThis.customElements.define('media-popover-portal', MediaPopoverPortal);
-}
-
-if (!globalThis.customElements.get('media-popover-positioner')) {
- globalThis.customElements.define('media-popover-positioner', MediaPopoverPositioner);
-}
-
-if (!globalThis.customElements.get('media-popover-popup')) {
- globalThis.customElements.define('media-popover-popup', MediaPopoverPopup);
-}
-
-export const Popover: {
- Root: typeof MediaPopoverRoot;
- Trigger: typeof MediaPopoverTrigger;
- Portal: typeof MediaPopoverPortal;
- Positioner: typeof MediaPopoverPositioner;
- Popup: typeof MediaPopoverPopup;
-} = {
- Root: MediaPopoverRoot,
- Trigger: MediaPopoverTrigger,
- Portal: MediaPopoverPortal,
- Positioner: MediaPopoverPositioner,
- Popup: MediaPopoverPopup,
-};
+export { Popover };
export default Popover;
diff --git a/packages/html/src/components/media-tooltip.ts b/packages/html/src/components/media-tooltip.ts
index c37fef64..1501e7b9 100644
--- a/packages/html/src/components/media-tooltip.ts
+++ b/packages/html/src/components/media-tooltip.ts
@@ -9,23 +9,26 @@ export class MediaTooltipRoot extends HTMLElement {
#hoverTimeout: ReturnType | null = null;
#cleanup: (() => void) | null = null;
#arrowElement: HTMLElement | null = null;
- #mousePosition = { x: 0, y: 0 };
+ #pointerPosition = { x: 0, y: 0 };
#transitionStatus: 'initial' | 'open' | 'close' | 'unmounted' = 'initial';
constructor() {
super();
- this.addEventListener('mouseenter', this);
- this.addEventListener('mouseleave', this);
- this.addEventListener('mousemove', this);
+
+ if (globalThis.matchMedia?.('(hover: hover)')?.matches) {
+ this.addEventListener('pointerenter', this);
+ this.addEventListener('pointerleave', this);
+ this.addEventListener('pointermove', this);
+ }
}
handleEvent(event: Event): void {
- if (event.type === 'mouseenter') {
- this.#handleMouseEnter();
- } else if (event.type === 'mouseleave') {
- this.#handleMouseLeave();
- } else if (event.type === 'mousemove') {
- this.#handleMouseMove(event as MouseEvent);
+ if (event.type === 'pointerenter') {
+ this.#handlePointerEnter();
+ } else if (event.type === 'pointerleave') {
+ this.#handlePointerLeave(event as PointerEvent);
+ } else if (event.type === 'pointermove') {
+ this.#handlePointerMove(event as PointerEvent);
}
}
@@ -157,34 +160,34 @@ export class MediaTooltipRoot extends HTMLElement {
width: 0,
height: 0,
top: triggerRect.top,
- right: this.#mousePosition.x,
+ right: this.#pointerPosition.x,
bottom: triggerRect.bottom,
- left: this.#mousePosition.x,
- x: this.#mousePosition.x,
+ left: this.#pointerPosition.x,
+ x: this.#pointerPosition.x,
y: triggerRect.top,
};
} else if (this.trackCursorAxis === 'y') {
return {
width: 0,
height: 0,
- top: this.#mousePosition.y,
+ top: this.#pointerPosition.y,
right: triggerRect.right,
- bottom: this.#mousePosition.y,
+ bottom: this.#pointerPosition.y,
left: triggerRect.left,
x: triggerRect.left,
- y: this.#mousePosition.y,
+ y: this.#pointerPosition.y,
};
} else {
// Track both axes (trackCursorAxis === 'both')
return {
width: 0,
height: 0,
- top: this.#mousePosition.y,
- right: this.#mousePosition.x,
- bottom: this.#mousePosition.y,
- left: this.#mousePosition.x,
- x: this.#mousePosition.x,
- y: this.#mousePosition.y,
+ top: this.#pointerPosition.y,
+ right: this.#pointerPosition.x,
+ bottom: this.#pointerPosition.y,
+ left: this.#pointerPosition.x,
+ x: this.#pointerPosition.x,
+ y: this.#pointerPosition.y,
};
}
},
@@ -232,23 +235,23 @@ export class MediaTooltipRoot extends HTMLElement {
}
}
- #handleMouseEnter(): void {
+ #handlePointerEnter(): void {
this.#clearHoverTimeout();
this.#hoverTimeout = globalThis.setTimeout(() => {
this.#setOpen(true);
}, this.delay);
}
- #handleMouseLeave(): void {
+ #handlePointerLeave(_event: PointerEvent): void {
this.#clearHoverTimeout();
this.#hoverTimeout = globalThis.setTimeout(() => {
this.#setOpen(false);
}, this.closeDelay);
}
- #handleMouseMove(event: MouseEvent): void {
+ #handlePointerMove(event: PointerEvent): void {
if (this.trackCursorAxis) {
- this.#mousePosition = { x: event.clientX, y: event.clientY };
+ this.#pointerPosition = { x: event.clientX, y: event.clientY };
if (this.#open) {
this.#updatePosition();
diff --git a/packages/html/src/skins/frosted/index.ts b/packages/html/src/skins/frosted/index.ts
index ae85dc5a..cab22679 100644
--- a/packages/html/src/skins/frosted/index.ts
+++ b/packages/html/src/skins/frosted/index.ts
@@ -70,27 +70,29 @@ export function getTemplateHTML() {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/packages/html/src/skins/frosted/styles.css b/packages/html/src/skins/frosted/styles.css
index 29772b12..489e70a3 100644
--- a/packages/html/src/skins/frosted/styles.css
+++ b/packages/html/src/skins/frosted/styles.css
@@ -325,7 +325,11 @@ preview-time-display {
transition-duration: 0ms;
}
-media-popover-popup {
+media-popover {
+ margin: 0;
+ border: none;
+ box-shadow: none;
+ background: transparent;
padding: 0.75rem 0.25rem;
border-radius: calc(infinity * 1px);
}
diff --git a/packages/html/src/skins/minimal/index.ts b/packages/html/src/skins/minimal/index.ts
index a2fe1975..fc2570d0 100644
--- a/packages/html/src/skins/minimal/index.ts
+++ b/packages/html/src/skins/minimal/index.ts
@@ -74,27 +74,29 @@ export function getTemplateHTML() {