mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-04 23:18:44 +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()
|
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 () => {
|
it('keeps the active texture and ready state when a replacement wallpaper fails', async () => {
|
||||||
const three = await import('three')
|
const three = await import('three')
|
||||||
const canvas = document.createElement('canvas')
|
const canvas = document.createElement('canvas')
|
||||||
|
|||||||
@@ -1930,7 +1930,6 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
|||||||
if (seen.has(element) || !element.isConnected || resolveGlassOpticalSurfaceMode(element) !== mode) return
|
if (seen.has(element) || !element.isConnected || resolveGlassOpticalSurfaceMode(element) !== mode) return
|
||||||
|
|
||||||
const rect = committedRect ?? getElementPresentationRect(element)
|
const rect = committedRect ?? getElementPresentationRect(element)
|
||||||
if (rect.width < 24 || rect.height < 24) return
|
|
||||||
|
|
||||||
seen.add(element)
|
seen.add(element)
|
||||||
candidates.push({ key: element, mode, owner, rect })
|
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)
|
const rect = committedSurfaces.get(clip.key)?.rect ?? getElementPresentationRect(clip.key)
|
||||||
if (rect.width < 24 || rect.height < 24) return []
|
|
||||||
|
|
||||||
return [{ ...clip, rect }]
|
return [{ ...clip, rect }]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInteractionClipRenderable(rect: GlassOpticalRect) {
|
||||||
|
return rect.width >= 24 && rect.height >= 24
|
||||||
|
}
|
||||||
|
|
||||||
function updateInteractionClips() {
|
function updateInteractionClips() {
|
||||||
const slotKeys = new Set(surfaceSlots.map(slot => slot.key))
|
const slotKeys = new Set(surfaceSlots.map(slot => slot.key))
|
||||||
const viewportX = presentationSpace === 'scroll' ? window.scrollX : 0
|
const viewportX = presentationSpace === 'scroll' ? window.scrollX : 0
|
||||||
@@ -2003,7 +2004,11 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
|||||||
inOverscan,
|
inOverscan,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(entry => entry.candidate.key === activeInteractionClip || entry.inOverscan)
|
.filter(
|
||||||
|
entry =>
|
||||||
|
isInteractionClipRenderable(entry.candidate.rect) &&
|
||||||
|
(entry.candidate.key === activeInteractionClip || entry.inOverscan),
|
||||||
|
)
|
||||||
.sort((left, right) => {
|
.sort((left, right) => {
|
||||||
const leftActive = left.candidate.key === activeInteractionClip
|
const leftActive = left.candidate.key === activeInteractionClip
|
||||||
const rightActive = right.candidate.key === activeInteractionClip
|
const rightActive = right.candidate.key === activeInteractionClip
|
||||||
@@ -2445,7 +2450,12 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
|
|||||||
if (!surface) return null
|
if (!surface) return null
|
||||||
|
|
||||||
const matchingClips = interactionClipRegistry
|
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)
|
.sort((left, right) => left.rect.width * left.rect.height - right.rect.width * right.rect.height)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user