Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,48 @@ describe('MouseGlowService', () => {
surface.remove();
});

it('only reads geometry for the selected surface', () => {
const surface = document.createElement('div');
surface.className = 'workspace-card';
surface.style.border = '1px solid black';
surface.style.borderRadius = '8px';
surface.style.background = 'black';
const readSurfaceGeometry = vi.fn(() => ({
bottom: 128,
height: 80,
left: 20,
right: 220,
top: 48,
width: 200,
x: 20,
y: 48,
toJSON: () => ({}),
}));
surface.getBoundingClientRect = readSurfaceGeometry;
const wrapper = document.createElement('div');
const content = document.createElement('code');
const readWrapperGeometry = vi.fn();
const readContentGeometry = vi.fn();
wrapper.getBoundingClientRect = readWrapperGeometry;
content.getBoundingClientRect = readContentGeometry;
wrapper.appendChild(content);
surface.appendChild(wrapper);
document.body.appendChild(surface);
service.initialize();

content.dispatchEvent(new MouseEvent('pointermove', {
bubbles: true,
clientX: 72,
clientY: 68,
}));
nextFrame?.(0);

expect(readContentGeometry).not.toHaveBeenCalled();
expect(readWrapperGeometry).not.toHaveBeenCalled();
expect(readSurfaceGeometry).toHaveBeenCalledTimes(1);
surface.remove();
});

it('keeps the glow up until the frame resolves where the pointer went', () => {
const surface = document.createElement('div');
surface.setAttribute('data-mouse-glow-surface', '');
Expand Down Expand Up @@ -249,6 +291,8 @@ describe('MouseGlowService', () => {
const stackingHost = document.createElement('div');
stackingHost.style.position = 'relative';
stackingHost.style.zIndex = '1';
const readStackingHostGeometry = vi.fn();
stackingHost.getBoundingClientRect = readStackingHostGeometry;
const surface = document.createElement('div');
surface.setAttribute('data-mouse-glow-surface', '');
surface.getBoundingClientRect = () => ({
Expand Down Expand Up @@ -295,7 +339,9 @@ describe('MouseGlowService', () => {

const overlay = document.getElementById('bitfun-mouse-glow-overlay');
expect(overlay?.hasAttribute('data-active')).toBe(true);
expect(overlay?.parentElement).toBe(stackingHost);
expect(overlay?.parentElement).toBe(document.body);
expect(overlay?.hasAttribute('data-local-position')).toBe(false);
expect(readStackingHostGeometry).not.toHaveBeenCalled();
expect(Number(window.getComputedStyle(listbox).zIndex)).toBeGreaterThan(49);

option.dispatchEvent(new MouseEvent('pointermove', {
Expand Down Expand Up @@ -359,7 +405,7 @@ describe('MouseGlowService', () => {
transformedHost.style.position = 'absolute';
transformedHost.style.zIndex = '100';
transformedHost.style.transform = 'translateX(-50%)';
transformedHost.getBoundingClientRect = () => ({
const readTransformedHostGeometry = vi.fn(() => ({
bottom: 700,
height: 200,
left: 100,
Expand All @@ -369,7 +415,8 @@ describe('MouseGlowService', () => {
x: 100,
y: 500,
toJSON: () => ({}),
});
}));
transformedHost.getBoundingClientRect = readTransformedHostGeometry;
const inputSurface = document.createElement('div');
inputSurface.setAttribute('data-mouse-glow-surface', '');
inputSurface.style.border = '1px solid black';
Expand Down Expand Up @@ -400,11 +447,12 @@ describe('MouseGlowService', () => {
nextFrame?.(0);

const overlay = document.getElementById('bitfun-mouse-glow-overlay');
expect(overlay?.parentElement).toBe(transformedHost);
expect(overlay?.hasAttribute('data-local-position')).toBe(true);
expect(overlay?.style.transform).toBe('translate3d(20px, 40px, 0)');
expect(overlay?.parentElement).toBe(document.body);
expect(overlay?.hasAttribute('data-local-position')).toBe(false);
expect(overlay?.style.transform).toBe('translate3d(120px, 540px, 0)');
expect(overlay?.style.getPropertyValue('--mouse-glow-local-x')).toBe('60px');
expect(overlay?.style.getPropertyValue('--mouse-glow-local-y')).toBe('20px');
expect(readTransformedHostGeometry).not.toHaveBeenCalled();
transformedHost.remove();
});

Expand Down
38 changes: 9 additions & 29 deletions src/web-ui/src/infrastructure/mouse-glow/core/MouseGlowService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,11 @@ export class MouseGlowService {
overlayHost,
style,
);
const overlayPosition = this.getOverlayPosition(geometry, overlayHost);
const overlayPosition = this.getOverlayPosition(
geometry,
overlayHost,
overlayHost === surface ? rect : null,
);
this.activeSurface = surface;
overlay.toggleAttribute('data-divider', dividerGeometry !== null);
overlay.toggleAttribute('data-local-position', overlayPosition.isLocal);
Expand Down Expand Up @@ -319,6 +323,7 @@ export class MouseGlowService {
private getOverlayPosition(
geometry: OverlayGeometry,
host: HTMLElement,
knownHostRect: DOMRect | null,
): { isLocal: boolean; left: number; top: number } {
if (host === document.body) {
return { isLocal: false, left: geometry.left, top: geometry.top };
Expand All @@ -329,7 +334,7 @@ export class MouseGlowService {
return { isLocal: false, left: geometry.left, top: geometry.top };
}

const hostRect = host.getBoundingClientRect();
const hostRect = knownHostRect ?? host.getBoundingClientRect();
const borderLeftWidth = parseFloat(hostStyle.borderLeftWidth) || 0;
const borderTopWidth = parseFloat(hostStyle.borderTopWidth) || 0;
return {
Expand Down Expand Up @@ -408,11 +413,6 @@ export class MouseGlowService {
return false;
}

const rect = element.getBoundingClientRect();
if (rect.width <= 0 || rect.height <= 0) {
return false;
}

if (!this.isVisibleElement(style)) {
return false;
}
Expand Down Expand Up @@ -441,7 +441,6 @@ export class MouseGlowService {
return false;
}

const rect = element.getBoundingClientRect();
if (!this.isVisibleElement(style)) {
return false;
}
Expand All @@ -451,7 +450,7 @@ export class MouseGlowService {
return true;
}

return this.hasDividerSemantics(element) && this.isLineLikeGeometry(rect);
return this.hasDividerSemantics(element);
}

private getDividerGeometry(
Expand Down Expand Up @@ -485,7 +484,7 @@ export class MouseGlowService {
}
}

if (this.hasDividerSemantics(element)) {
if (this.hasDividerSemantics(element) && this.isLineLikeGeometry(rect)) {
return {
height: Math.max(rect.height, 1),
left: rect.left,
Expand Down Expand Up @@ -557,9 +556,6 @@ export class MouseGlowService {
if (this.isFloatingLayer(current)) {
return current;
}
if (this.createsStackingContext(current)) {
return current;
}
current = current.parentElement;
}
return document.body;
Expand All @@ -585,22 +581,6 @@ export class MouseGlowService {
);
}

private createsStackingContext(element: HTMLElement): boolean {
const style = window.getComputedStyle(element);
const positionedWithZIndex =
style.position !== 'static' && style.zIndex !== 'auto';

return (
positionedWithZIndex
|| style.position === 'fixed'
|| style.position === 'sticky'
|| style.isolation === 'isolate'
|| (style.opacity !== '' && style.opacity !== '1')
|| (style.mixBlendMode !== '' && style.mixBlendMode !== 'normal')
|| style.willChange.includes('opacity')
);
}

private isVisibleElement(style: CSSStyleDeclaration): boolean {
return (
style.display !== 'none'
Expand Down