Scroll lock utility
This commit is contained in:
parent
7e201f6af6
commit
18f4869b20
@ -0,0 +1,37 @@
|
||||
// Global lock counter so nested modals don't prematurely restore scroll
|
||||
let lockCount = 0;
|
||||
let originalOverflow = "";
|
||||
|
||||
export interface ScrollLock {
|
||||
lock: () => void;
|
||||
unlock: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents body scroll. Uses a reference count so nested overlays
|
||||
* (e.g. a dialog inside a drawer) don't prematurely restore scrolling.
|
||||
*/
|
||||
export function createScrollLock(): ScrollLock {
|
||||
let locked = false;
|
||||
|
||||
return {
|
||||
lock() {
|
||||
if (locked) return;
|
||||
locked = true;
|
||||
if (lockCount === 0) {
|
||||
originalOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
lockCount++;
|
||||
},
|
||||
unlock() {
|
||||
if (!locked) return;
|
||||
locked = false;
|
||||
lockCount = Math.max(0, lockCount - 1);
|
||||
if (lockCount === 0) {
|
||||
document.body.style.overflow = originalOverflow;
|
||||
originalOverflow = "";
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
2
packages/core/src/utilities/scroll-lock/index.ts
Normal file
2
packages/core/src/utilities/scroll-lock/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { createScrollLock } from "./create-scroll-lock";
|
||||
export type { ScrollLock } from "./create-scroll-lock";
|
||||
45
packages/core/tests/utilities/scroll-lock.test.ts
Normal file
45
packages/core/tests/utilities/scroll-lock.test.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { createRoot } from "solid-js";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { createScrollLock } from "../../src/utilities/scroll-lock/create-scroll-lock";
|
||||
|
||||
describe("createScrollLock", () => {
|
||||
afterEach(() => {
|
||||
document.body.style.overflow = "";
|
||||
document.body.style.paddingRight = "";
|
||||
});
|
||||
|
||||
it("sets overflow hidden on body when locked", () => {
|
||||
createRoot((dispose) => {
|
||||
const lock = createScrollLock();
|
||||
lock.lock();
|
||||
expect(document.body.style.overflow).toBe("hidden");
|
||||
lock.unlock();
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("restores overflow on unlock", () => {
|
||||
document.body.style.overflow = "auto";
|
||||
createRoot((dispose) => {
|
||||
const lock = createScrollLock();
|
||||
lock.lock();
|
||||
lock.unlock();
|
||||
expect(document.body.style.overflow).toBe("auto");
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("handles multiple locks — only unlocks when all release", () => {
|
||||
createRoot((dispose) => {
|
||||
const lockA = createScrollLock();
|
||||
const lockB = createScrollLock();
|
||||
lockA.lock();
|
||||
lockB.lock();
|
||||
lockA.unlock();
|
||||
expect(document.body.style.overflow).toBe("hidden"); // still locked by B
|
||||
lockB.unlock();
|
||||
expect(document.body.style.overflow).toBe(""); // now unlocked
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user