`SvelteResizeObserver` lived in `$libs/dom` as a generic ResizeObserver wrapper with manual `refresh()` / `destroy()` lifecycle. Across the repo it had zero real consumers — only its own test file imported it. The Svelte-aware ResizeObserver in active use is `ResizeObserver$` in `src/suix/soma/layers/resize-observer.svelte.ts`, which uses `$effect` for auto-cleanup and is consumed by accordion. Removing the unused module keeps `$libs/dom` honest as "the surface nobody can reach for a duplicate ResizeObserver implementation when they could just consume the soma layer (or, eventually, an `$adom` helper that mirrors `BodyScrollLock`)". Verification: - `npx vitest run src/libs/dom src/arts/adom`: 9 / 9 files, 37 / 37 tests green (down from 10 / 40 — the dropped 3 tests covered the removed class). - `node scripts/check-aliases.mjs`: clean (only the pre-existing PERMUTATION_RUNNER.md hit, unrelated). - `npm run check`: 61 / 0 / 17 unchanged from before (the residual errors are all in untracked src/suix/* and predate this commit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>master
parent
b7b338a616
commit
af38c4c170
@ -1,58 +0,0 @@
|
|||||||
export type ResizeObservedNode = HTMLElement | null | undefined
|
|
||||||
export type ResizeObservedNodeGetter = () => ResizeObservedNode
|
|
||||||
|
|
||||||
function canObserveResize(): boolean {
|
|
||||||
return typeof window !== 'undefined' && typeof ResizeObserver !== 'undefined'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SvelteResizeObserver {
|
|
||||||
readonly node: ResizeObservedNodeGetter
|
|
||||||
readonly onResize: () => void
|
|
||||||
|
|
||||||
private observer: ResizeObserver | null = null
|
|
||||||
private observedNode: HTMLElement | null = null
|
|
||||||
private rAF = 0
|
|
||||||
|
|
||||||
constructor(node: ResizeObservedNodeGetter, onResize: () => void) {
|
|
||||||
this.node = node
|
|
||||||
this.onResize = onResize
|
|
||||||
this.refresh()
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh = () => {
|
|
||||||
if (!canObserveResize()) return
|
|
||||||
|
|
||||||
const nextNode = this.node() ?? null
|
|
||||||
if (nextNode === this.observedNode) return
|
|
||||||
|
|
||||||
this.disconnect()
|
|
||||||
if (!nextNode) return
|
|
||||||
|
|
||||||
this.observedNode = nextNode
|
|
||||||
this.observer = new ResizeObserver(() => {
|
|
||||||
if (typeof window.requestAnimationFrame === 'function') {
|
|
||||||
window.cancelAnimationFrame(this.rAF)
|
|
||||||
this.rAF = window.requestAnimationFrame(this.onResize)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onResize()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.observer.observe(nextNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy = () => {
|
|
||||||
this.disconnect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private disconnect() {
|
|
||||||
if (typeof window !== 'undefined' && typeof window.cancelAnimationFrame === 'function') {
|
|
||||||
window.cancelAnimationFrame(this.rAF)
|
|
||||||
}
|
|
||||||
this.rAF = 0
|
|
||||||
this.observer?.disconnect()
|
|
||||||
this.observer = null
|
|
||||||
this.observedNode = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
// @vitest-environment jsdom
|
|
||||||
|
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
||||||
|
|
||||||
import { SvelteResizeObserver } from '../resize-observer';
|
|
||||||
|
|
||||||
type ResizeObserverCallbackLike = ConstructorParameters<typeof ResizeObserver>[0];
|
|
||||||
|
|
||||||
class MockResizeObserver {
|
|
||||||
static instances: MockResizeObserver[] = [];
|
|
||||||
|
|
||||||
readonly observed: Element[] = [];
|
|
||||||
disconnected = false;
|
|
||||||
readonly callback: ResizeObserverCallbackLike;
|
|
||||||
|
|
||||||
constructor(callback: ResizeObserverCallbackLike) {
|
|
||||||
this.callback = callback;
|
|
||||||
MockResizeObserver.instances.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
observe = (element: Element) => {
|
|
||||||
this.observed.push(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
disconnect = () => {
|
|
||||||
this.disconnected = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
trigger() {
|
|
||||||
this.callback([] as ResizeObserverEntry[], this as unknown as ResizeObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('libs/dom resize observer', () => {
|
|
||||||
const originalResizeObserver = globalThis.ResizeObserver;
|
|
||||||
const originalRequestAnimationFrame = window.requestAnimationFrame;
|
|
||||||
const originalCancelAnimationFrame = window.cancelAnimationFrame;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
MockResizeObserver.instances = [];
|
|
||||||
(globalThis as typeof globalThis & { ResizeObserver: typeof ResizeObserver }).ResizeObserver =
|
|
||||||
MockResizeObserver as unknown as typeof ResizeObserver;
|
|
||||||
window.requestAnimationFrame = ((callback: FrameRequestCallback) => {
|
|
||||||
callback(0);
|
|
||||||
return 1;
|
|
||||||
}) as typeof window.requestAnimationFrame;
|
|
||||||
window.cancelAnimationFrame = vi.fn() as typeof window.cancelAnimationFrame;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
const globals = globalThis as { ResizeObserver?: typeof ResizeObserver };
|
|
||||||
if (originalResizeObserver) {
|
|
||||||
globals.ResizeObserver = originalResizeObserver;
|
|
||||||
} else {
|
|
||||||
delete globals.ResizeObserver;
|
|
||||||
}
|
|
||||||
window.requestAnimationFrame = originalRequestAnimationFrame;
|
|
||||||
window.cancelAnimationFrame = originalCancelAnimationFrame;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('observes the provided node and invokes the callback on resize', async () => {
|
|
||||||
const node = document.createElement('div');
|
|
||||||
const onResize = vi.fn();
|
|
||||||
|
|
||||||
new SvelteResizeObserver(() => node, onResize);
|
|
||||||
|
|
||||||
expect(MockResizeObserver.instances).toHaveLength(1);
|
|
||||||
expect(MockResizeObserver.instances[0]?.observed).toEqual([node]);
|
|
||||||
|
|
||||||
MockResizeObserver.instances[0]?.trigger();
|
|
||||||
|
|
||||||
expect(onResize).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not create an observer when the target is null', async () => {
|
|
||||||
new SvelteResizeObserver(() => null, vi.fn());
|
|
||||||
|
|
||||||
expect(MockResizeObserver.instances).toHaveLength(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can rebind to a new node and disconnect on destroy', () => {
|
|
||||||
const first = document.createElement('div');
|
|
||||||
const second = document.createElement('div');
|
|
||||||
let current = first;
|
|
||||||
|
|
||||||
const resizeObserver = new SvelteResizeObserver(() => current, vi.fn());
|
|
||||||
|
|
||||||
expect(MockResizeObserver.instances).toHaveLength(1);
|
|
||||||
expect(MockResizeObserver.instances[0]?.observed).toEqual([first]);
|
|
||||||
|
|
||||||
current = second;
|
|
||||||
resizeObserver.refresh();
|
|
||||||
|
|
||||||
expect(MockResizeObserver.instances).toHaveLength(2);
|
|
||||||
expect(MockResizeObserver.instances[0]?.disconnected).toBe(true);
|
|
||||||
expect(MockResizeObserver.instances[1]?.observed).toEqual([second]);
|
|
||||||
|
|
||||||
resizeObserver.destroy();
|
|
||||||
|
|
||||||
expect(MockResizeObserver.instances[1]?.disconnected).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Reference in new issue