mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-10 16:13:28 +08:00
fix(glass): preserve collapsed interaction clips (#601)
This commit is contained in:
@@ -1960,6 +1960,64 @@ describe('glass optical surface discovery', () => {
|
||||
scope.stop()
|
||||
})
|
||||
|
||||
it('restores a temporarily collapsed interaction clip without rebuilding registry membership', async () => {
|
||||
vi.spyOn(window, 'innerWidth', 'get').mockReturnValue(1200)
|
||||
vi.spyOn(window, 'innerHeight', 'get').mockReturnValue(800)
|
||||
const three = await import('three')
|
||||
const render = vi.spyOn(three.WebGLRenderer.prototype, 'render')
|
||||
const outerSurface = appendOpticalSurface('v-card', { height: 500, width: 900, x: 80, y: 100 })
|
||||
outerSurface.dataset.glassOpticalSurface = ''
|
||||
const card = document.createElement('article')
|
||||
card.className = 'app-hover-lift-card'
|
||||
setOpticalSurfaceBounds(card, { height: 180, width: 360, x: 100, y: 140 })
|
||||
outerSurface.append(card)
|
||||
const querySelectorAll = vi.spyOn(outerSurface, 'querySelectorAll')
|
||||
const scope = effectScope()
|
||||
const renderer = scope.run(() =>
|
||||
useGlassOpticalRenderer({
|
||||
active: ref(true),
|
||||
appearance: ref('clear'),
|
||||
canvas: ref(document.createElement('canvas')),
|
||||
quality: ref('balanced'),
|
||||
routeKey: ref('/dashboard'),
|
||||
surfaceSpace: 'scroll',
|
||||
tintColor: ref('#8D51F9'),
|
||||
wallpaperUrl: ref('https://example.com/wallpaper.jpg'),
|
||||
}),
|
||||
)
|
||||
|
||||
await vi.waitFor(() => expect(renderer?.state.value).toBe('ready'))
|
||||
window.dispatchEvent(new MouseEvent('pointermove', { clientX: 160, clientY: 180 }))
|
||||
await vi.waitFor(() => expect(render).toHaveBeenCalled())
|
||||
const getInteractionRectCount = () => {
|
||||
const scene = render.mock.calls.at(-1)?.[0] as unknown as {
|
||||
children: Array<{
|
||||
material: {
|
||||
uniforms: {
|
||||
uInteractionRectCount: { value: number }
|
||||
}
|
||||
}
|
||||
}>
|
||||
}
|
||||
|
||||
return scene.children[0].material.uniforms.uInteractionRectCount.value
|
||||
}
|
||||
|
||||
expect(getInteractionRectCount()).toBe(1)
|
||||
querySelectorAll.mockClear()
|
||||
|
||||
setOpticalSurfaceBounds(card, { height: 12, width: 12, x: 100, y: 140 })
|
||||
ResizeObserverMock.instances.forEach(observer => observer.trigger())
|
||||
await vi.waitFor(() => expect(getInteractionRectCount()).toBe(0))
|
||||
|
||||
setOpticalSurfaceBounds(card, { height: 180, width: 360, x: 100, y: 140 })
|
||||
ResizeObserverMock.instances.forEach(observer => observer.trigger())
|
||||
await vi.waitFor(() => expect(getInteractionRectCount()).toBe(1))
|
||||
|
||||
expect(querySelectorAll).not.toHaveBeenCalled()
|
||||
scope.stop()
|
||||
})
|
||||
|
||||
it('keeps the active texture and ready state when a replacement wallpaper fails', async () => {
|
||||
const three = await import('three')
|
||||
const canvas = document.createElement('canvas')
|
||||
|
||||
@@ -1930,7 +1930,6 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
||||
if (seen.has(element) || !element.isConnected || resolveGlassOpticalSurfaceMode(element) !== mode) return
|
||||
|
||||
const rect = committedRect ?? getElementPresentationRect(element)
|
||||
if (rect.width < 24 || rect.height < 24) return
|
||||
|
||||
seen.add(element)
|
||||
candidates.push({ key: element, mode, owner, rect })
|
||||
@@ -1968,12 +1967,14 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
||||
}
|
||||
|
||||
const rect = committedSurfaces.get(clip.key)?.rect ?? getElementPresentationRect(clip.key)
|
||||
if (rect.width < 24 || rect.height < 24) return []
|
||||
|
||||
return [{ ...clip, rect }]
|
||||
})
|
||||
}
|
||||
|
||||
function isInteractionClipRenderable(rect: GlassOpticalRect) {
|
||||
return rect.width >= 24 && rect.height >= 24
|
||||
}
|
||||
|
||||
function updateInteractionClips() {
|
||||
const slotKeys = new Set(surfaceSlots.map(slot => slot.key))
|
||||
const viewportX = presentationSpace === 'scroll' ? window.scrollX : 0
|
||||
@@ -2003,7 +2004,11 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
||||
inOverscan,
|
||||
}
|
||||
})
|
||||
.filter(entry => entry.candidate.key === activeInteractionClip || entry.inOverscan)
|
||||
.filter(
|
||||
entry =>
|
||||
isInteractionClipRenderable(entry.candidate.rect) &&
|
||||
(entry.candidate.key === activeInteractionClip || entry.inOverscan),
|
||||
)
|
||||
.sort((left, right) => {
|
||||
const leftActive = left.candidate.key === activeInteractionClip
|
||||
const rightActive = right.candidate.key === activeInteractionClip
|
||||
@@ -2445,7 +2450,12 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
||||
if (!surface) return null
|
||||
|
||||
const matchingClips = interactionClipRegistry
|
||||
.filter(candidate => candidate.owner === surface.key && rectContainsPoint(candidate.rect, x, y))
|
||||
.filter(
|
||||
candidate =>
|
||||
candidate.owner === surface.key &&
|
||||
isInteractionClipRenderable(candidate.rect) &&
|
||||
rectContainsPoint(candidate.rect, x, y),
|
||||
)
|
||||
.sort((left, right) => left.rect.width * left.rect.height - right.rect.width * right.rect.height)
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user