[](https://github.com/KingSora/OverlayScrollbars)
- [](https://github.com/sveltejs/svelte)
+ [](https://github.com/sveltejs/svelte)
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
[](https://www.npmjs.com/package/overlayscrollbars-svelte)
[](#)
@@ -93,7 +93,7 @@ Additionally it has custom optional properties:
Additionally to the `events` property the `OverlayScrollbarsComponent` emits "native" Svelte events. To prevent name collisions with DOM events the events have a `os` prefix.
-> __Note__: It doesn't matter whether you use the `events` property or the Svelte events or both.
+> __Note__: This feature is [deprecated](https://svelte.dev/docs/svelte/v5-migration-guide#Event-changes) since `Svelte5`. The `OverlayScrollbarsComponent` still supports it to ease migration. It doesn't matter whether you use the `events` property or the Svelte events or both.
```jsx
// example usage
@@ -124,6 +124,61 @@ The ref object has two properties:
- `osInstance`: a function which returns the OverlayScrollbars instance.
- `getElement`: a function which returns the root element.
+## Primitive
+
+In case the `OverlayScrollbarsComponent` is not enough, you can also use the `useOverlayScrollbars` primitive:
+
+```svelte
+
+
+
+```
+
+The primitive is for advanced usage and lets you control the whole initialization process. This is useful if you want to integrate it with other plugins.
+
+### Parameters
+
+Parameters are optional and similar to the `OverlayScrollbarsComponent`.
+Its an `object` with optional properties:
+
+- `options`: accepts an `object` which represents the OverlayScrollbars options.
+- `events`: accepts an `object` which represents the OverlayScrollbars events.
+- `defer`: accepts an `boolean` or `object`. Defers the initialization to a point in time when the browser is idle.
+
+> __Note__: You can also pass a `function` as parameter which returns the object in case the object itself is reactive. This also applies to all fields.
+
+### Return
+
+The `useOverlayScrollbars` primitive returns a `tuple` with two values:
+
+- The first value is the `initialization` function, it takes one argument which is the `InitializationTarget`.
+- The second value is a function which returns the current OverlayScrollbars instance or `null` if not initialized.
+
## License
MIT
diff --git a/packages/overlayscrollbars-svelte/package.json b/packages/overlayscrollbars-svelte/package.json
index ba81279c..e93e6fea 100644
--- a/packages/overlayscrollbars-svelte/package.json
+++ b/packages/overlayscrollbars-svelte/package.json
@@ -2,7 +2,7 @@
"name": "overlayscrollbars-svelte",
"private": true,
"type": "module",
- "version": "0.5.4",
+ "version": "0.5.5",
"description": "OverlayScrollbars for Svelte.",
"author": "Rene Haas | KingSora",
"license": "MIT",
@@ -35,7 +35,7 @@
},
"peerDependencies": {
"overlayscrollbars": "^2.0.0",
- "svelte": ">=3.54.0"
+ "svelte": "^5.0.0"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^4.0.0",
diff --git a/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.svelte b/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.svelte
index 8ee65259..56fe3182 100644
--- a/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.svelte
+++ b/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.svelte
@@ -1,65 +1,45 @@
-
-
+{#snippet content()}
{#if element === 'body'}
-
+ {@render children?.()}
{:else}
-
+ {@render children?.()}
{/if}
-
+{/snippet}
+
+{#if typeof element === 'string'}
+
+ {@render content()}
+
+{:else}
+ {@const Tag = element as ValidSvelteComponent}
+
+ {@render content()}
+
+{/if}
diff --git a/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.types.ts b/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.types.ts
index 47f35881..0717f163 100644
--- a/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.types.ts
+++ b/packages/overlayscrollbars-svelte/src/OverlayScrollbarsComponent.types.ts
@@ -1,19 +1,54 @@
import type { OverlayScrollbars, PartialOptions, EventListeners } from 'overlayscrollbars';
+import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
+import type { Component, ComponentProps, Snippet } from 'svelte';
-export interface Props {
- /** Tag of the root element. */
- element?: string;
- /** OverlayScrollbars options. */
- options?: PartialOptions | false | null;
- /** OverlayScrollbars events. */
- events?: EventListeners | false | null;
- /** Whether to defer the initialization to a point in time when the browser is idle. (or to the next frame if `window.requestIdleCallback` is not supported) */
- defer?: boolean | IdleRequestOptions;
-}
+export type ValidSvelteHTMLTags = Extract
;
+export type ValidSvelteComponent =
+ Component<
+ {
+ children?: Snippet;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ [prop: string | number | symbol]: any;
+ },
+ {
+ getElement: () => SvelteHTMLElementFromHtmlAttributes | undefined;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ [prop: string | number | symbol]: any;
+ }
+ >;
+export type ValidSvelteElement = ValidSvelteHTMLTags | ValidSvelteComponent;
+export type ValidSvelteElementProps =
+ T extends ValidSvelteComponent
+ ? ComponentProps
+ : T extends keyof SvelteHTMLElements
+ ? SvelteHTMLElements[T]
+ : // eslint-disable-next-line @typescript-eslint/no-empty-object-type
+ {};
+export type ValidSvelteHTMLElement =
+ T extends ValidSvelteComponent
+ ? Exclude['getElement']>, undefined>
+ : T extends keyof SvelteHTMLElements
+ ? SvelteHTMLElementFromHtmlAttributes
+ : HTMLElement;
+
+type SvelteHTMLElementFromHtmlAttributes =
+ E extends HTMLAttributes ? (EventTarget extends El ? HTMLElement : El) : HTMLElement;
+
+export type OverlayScrollbarsComponentProps =
+ ValidSvelteElementProps & {
+ /** Tag of the root element. */
+ element?: T;
+ /** OverlayScrollbars options. */
+ options?: PartialOptions | false | null;
+ /** OverlayScrollbars events. */
+ events?: EventListeners | false | null;
+ /** Whether to defer the initialization to a point in time when the browser is idle. (or to the next frame if `window.requestIdleCallback` is not supported) */
+ defer?: boolean | IdleRequestOptions;
+ };
-export interface Ref {
+export interface OverlayScrollbarsComponentRef {
/** Returns the OverlayScrollbars instance or null if not initialized. */
osInstance(): OverlayScrollbars | null;
/** Returns the root element. */
- getElement(): HTMLElement | null;
+ getElement(): ValidSvelteHTMLElement | null;
}
diff --git a/packages/overlayscrollbars-svelte/src/createDefer.ts b/packages/overlayscrollbars-svelte/src/createDefer.ts
deleted file mode 100644
index 8cbb102e..00000000
--- a/packages/overlayscrollbars-svelte/src/createDefer.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import type { Props } from './OverlayScrollbarsComponent.types';
-
-export type Defer = [
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- requestDefer: (callback: () => any, options?: Props['defer']) => void,
- cancelDefer: () => void,
-];
-
-export const createDefer = (): Defer => {
- /* c8 ignore start */
- if (typeof window === 'undefined') {
- // mock ssr calls with "noop"
-
- const noop = () => {};
- return [noop, noop];
- }
- /* c8 ignore end */
-
- let idleId: number;
- let rafId: number;
- const wnd = window;
- const idleSupported = typeof wnd.requestIdleCallback === 'function';
- const rAF = wnd.requestAnimationFrame;
- const cAF = wnd.cancelAnimationFrame;
- const rIdle = idleSupported ? wnd.requestIdleCallback : rAF;
- const cIdle = idleSupported ? wnd.cancelIdleCallback : cAF;
- const clear = () => {
- cIdle(idleId);
- cAF(rafId);
- };
-
- return [
- (callback, options) => {
- clear();
- idleId = rIdle(
- idleSupported
- ? () => {
- clear();
- // inside idle its best practice to use rAF to change DOM for best performance
- rafId = rAF(callback);
- }
- : callback,
- typeof options === 'object' ? options : { timeout: 2233 }
- );
- },
- clear,
- ];
-};
diff --git a/packages/overlayscrollbars-svelte/src/overlayscrollbars-svelte.ts b/packages/overlayscrollbars-svelte/src/overlayscrollbars-svelte.ts
index 91eb2fc2..b7cd53ac 100644
--- a/packages/overlayscrollbars-svelte/src/overlayscrollbars-svelte.ts
+++ b/packages/overlayscrollbars-svelte/src/overlayscrollbars-svelte.ts
@@ -1 +1,3 @@
export { default as OverlayScrollbarsComponent } from './OverlayScrollbarsComponent.svelte';
+export * from './OverlayScrollbarsComponent.types';
+export * from './useOverlayScrollbars.svelte';
diff --git a/packages/overlayscrollbars-svelte/src/useOverlayScrollbars.svelte.ts b/packages/overlayscrollbars-svelte/src/useOverlayScrollbars.svelte.ts
new file mode 100644
index 00000000..3a1a9330
--- /dev/null
+++ b/packages/overlayscrollbars-svelte/src/useOverlayScrollbars.svelte.ts
@@ -0,0 +1,142 @@
+import { untrack } from 'svelte';
+import { OverlayScrollbars, type InitializationTarget } from 'overlayscrollbars';
+import type {
+ OverlayScrollbarsComponentProps,
+ OverlayScrollbarsComponentRef,
+} from './OverlayScrollbarsComponent.types';
+
+export interface UseOverlayScrollbarsParams {
+ /** OverlayScrollbars options. */
+ options?:
+ | OverlayScrollbarsComponentProps['options']
+ | Accessor;
+ /** OverlayScrollbars events. */
+ events?:
+ | OverlayScrollbarsComponentProps['events']
+ | Accessor;
+ /** Whether to defer the initialization to a point in time when the browser is idle. (or to the next frame if `window.requestIdleCallback` is not supported) */
+ defer?:
+ | OverlayScrollbarsComponentProps['defer']
+ | Accessor;
+}
+
+export type UseOverlayScrollbarsInitialization = (target: InitializationTarget) => void;
+
+export type UseOverlayScrollbarsInstance = () => ReturnType<
+ OverlayScrollbarsComponentRef['osInstance']
+>;
+
+type Accessor = () => T;
+
+type Defer = [
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ requestDefer: (callback: () => any, options?: OverlayScrollbarsComponentProps['defer']) => void,
+ cancelDefer: () => void,
+];
+
+const createDefer = (): Defer => {
+ /* c8 ignore start */
+ if (typeof window === 'undefined') {
+ // mock ssr calls with "noop"
+
+ const noop = () => {};
+ return [noop, noop];
+ }
+ /* c8 ignore end */
+
+ let idleId: number;
+ let rafId: number;
+ const wnd = window;
+ const idleSupported = typeof wnd.requestIdleCallback === 'function';
+ const rAF = wnd.requestAnimationFrame;
+ const cAF = wnd.cancelAnimationFrame;
+ const rIdle = idleSupported ? wnd.requestIdleCallback : rAF;
+ const cIdle = idleSupported ? wnd.cancelIdleCallback : cAF;
+ const clear = () => {
+ cIdle(idleId);
+ cAF(rafId);
+ };
+
+ return [
+ (callback, options) => {
+ clear();
+ idleId = rIdle(
+ idleSupported
+ ? () => {
+ clear();
+ // inside idle its best practice to use rAF to change DOM for best performance
+ rafId = rAF(callback);
+ }
+ : callback,
+ typeof options === 'object' ? options : { timeout: 2233 }
+ );
+ },
+ clear,
+ ];
+};
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const isAccessor = (obj: any): obj is Accessor => typeof obj === 'function';
+const unwrapAccessor = (obj: Accessor | T): T => (isAccessor(obj) ? obj() : obj);
+
+export const useOverlayScrollbars = (
+ params?: UseOverlayScrollbarsParams | Accessor
+): [UseOverlayScrollbarsInitialization, UseOverlayScrollbarsInstance] => {
+ let instance: ReturnType = null;
+ const [requestDefer, clearDefer] = createDefer();
+ const unwrappedParams = unwrapAccessor(params || {});
+ const options: OverlayScrollbarsComponentProps['options'] = $derived(
+ unwrapAccessor(unwrappedParams.options)
+ );
+ const events: OverlayScrollbarsComponentProps['events'] = $derived(
+ unwrapAccessor(unwrappedParams.events)
+ );
+ const defer: OverlayScrollbarsComponentProps['defer'] = $derived(
+ unwrapAccessor(unwrappedParams.defer)
+ );
+
+ $effect.pre(() => {
+ const currOptions = options;
+
+ if (OverlayScrollbars.valid(instance)) {
+ instance.options(currOptions || {}, true);
+ }
+ });
+
+ $effect.pre(() => {
+ const currEvents = events;
+
+ if (OverlayScrollbars.valid(instance)) {
+ instance.on(currEvents || {}, true);
+ }
+ });
+
+ $effect(() => {
+ return () => {
+ clearDefer();
+ instance?.destroy();
+ };
+ });
+
+ return [
+ (target) => {
+ // if already initialized do nothing
+ if (OverlayScrollbars.valid(instance)) {
+ return instance;
+ }
+
+ const currOptions = untrack(() => options);
+ const currEvents = untrack(() => events);
+ const currDefer = untrack(() => defer);
+ const init = () =>
+ (instance = OverlayScrollbars(target, currOptions || {}, currEvents || {}));
+
+ if (currDefer) {
+ requestDefer(init, currDefer);
+ } else {
+ init();
+ }
+ },
+ () => instance,
+ ];
+};
diff --git a/packages/overlayscrollbars-svelte/svelte.config.js b/packages/overlayscrollbars-svelte/svelte.config.js
index f8a8b458..32afd4eb 100644
--- a/packages/overlayscrollbars-svelte/svelte.config.js
+++ b/packages/overlayscrollbars-svelte/svelte.config.js
@@ -1,18 +1,13 @@
-import preprocess from 'svelte-preprocess';
+import { sveltePreprocess } from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
- preprocess: preprocess({ sourceMap: true }),
+ preprocess: sveltePreprocess({ sourceMap: true }),
compilerOptions: {
enableSourcemap: true,
},
- kit: {
- alias: {
- '~/*': './src/*',
- },
- },
};
export default config;
diff --git a/packages/overlayscrollbars-svelte/test/dom/Component.svelte b/packages/overlayscrollbars-svelte/test/dom/Component.svelte
deleted file mode 100644
index 0f8abf11..00000000
--- a/packages/overlayscrollbars-svelte/test/dom/Component.svelte
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
- hello svelte
- {#if children === 0}empty
{/if}
- {#each [...Array(children).keys()] as child}
-
- {/each}
-
-
-
- props
diff --git a/packages/overlayscrollbars-svelte/test/dom/OverlayScrollbarsComponent.test.ts b/packages/overlayscrollbars-svelte/test/dom/OverlayScrollbarsComponent.test.ts
index 4857002d..954a2007 100644
--- a/packages/overlayscrollbars-svelte/test/dom/OverlayScrollbarsComponent.test.ts
+++ b/packages/overlayscrollbars-svelte/test/dom/OverlayScrollbarsComponent.test.ts
@@ -2,10 +2,11 @@ import { describe, test, beforeEach, afterEach, expect, vi } from 'vitest';
import { cleanup, render, screen, fireEvent } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { OverlayScrollbars } from 'overlayscrollbars';
-import type { Ref } from '../../src/OverlayScrollbarsComponent.types';
+import type { OverlayScrollbarsComponentRef } from '../../src/OverlayScrollbarsComponent.types';
import { OverlayScrollbarsComponent } from '../../src/overlayscrollbars-svelte';
-import Component from './Component.svelte';
-import ComponentBody from './ComponentBody.svelte';
+import TestComponent from './TestComponent.svelte';
+import TestElement from './TestElement.svelte';
+import TestComponentBody from './TestComponentBody.svelte';
const getComputedStyleOriginal = window.getComputedStyle;
vi.stubGlobal(
@@ -51,8 +52,10 @@ describe('OverlayScrollbarsComponent', () => {
test('correct root element with instance', async () => {
const elementA = 'code';
const elementB = 'span';
+ const elementC = TestElement;
+
let osInstance;
- const { container } = render(Component);
+ const { container } = render(TestComponent);
expect(container).not.toBeEmptyDOMElement();
expect(container.querySelector('div')).toBe(container.firstElementChild); // default is div
@@ -88,6 +91,19 @@ describe('OverlayScrollbarsComponent', () => {
osInstance = OverlayScrollbars(container.firstElementChild as HTMLElement);
expect(osInstance).toBeDefined();
expect(OverlayScrollbars.valid(osInstance)).toBe(true);
+
+ await fireEvent(
+ screen.getByText('props'),
+ new CustomEvent('osProps', {
+ detail: { element: elementC },
+ })
+ );
+ expect(container.querySelector('section')).toBe(container.firstElementChild);
+
+ expect(OverlayScrollbars.valid(osInstance)).toBe(false); // prev instance is destroyed
+ osInstance = OverlayScrollbars(container.firstElementChild as HTMLElement);
+ expect(osInstance).toBeDefined();
+ expect(OverlayScrollbars.valid(osInstance)).toBe(true);
});
test('data-overlayscrollbars-initialize', async () => {
@@ -97,14 +113,14 @@ describe('OverlayScrollbarsComponent', () => {
});
test('children', () => {
- const { container } = render(Component);
+ const { container } = render(TestComponent);
expect(screen.getByText(/hello/)).toBeInTheDocument();
expect(screen.getByText(/svelte/)).toBeInTheDocument();
expect(screen.getByText(/svelte/).parentElement).not.toBe(container.firstElementChild);
});
test('dynamic children', async () => {
- render(Component);
+ render(TestComponent);
const addBtn = screen.getByText('add');
const removeBtn = screen.getByText('remove');
const initialElement = screen.getByText('0');
@@ -122,7 +138,7 @@ describe('OverlayScrollbarsComponent', () => {
});
test('className', async () => {
- const { container } = render(Component, {
+ const { container } = render(TestComponent, {
props: {
className: 'overlay scrollbars',
},
@@ -141,7 +157,7 @@ describe('OverlayScrollbarsComponent', () => {
});
test('style', async () => {
- const { container } = render(Component, {
+ const { container } = render(TestComponent, {
props: {
style: 'width: 22px',
},
@@ -162,7 +178,7 @@ describe('OverlayScrollbarsComponent', () => {
describe('deferred initialization', () => {
test('basic defer', () => {
- const { container } = render(Component, {
+ const { container } = render(TestComponent, {
props: {
defer: true,
},
@@ -176,7 +192,7 @@ describe('OverlayScrollbarsComponent', () => {
});
test('options defer', () => {
- const { container } = render(Component, {
+ const { container } = render(TestComponent, {
props: {
defer: { timeout: 0 },
},
@@ -194,7 +210,7 @@ describe('OverlayScrollbarsComponent', () => {
// @ts-ignore
window.requestIdleCallback = undefined;
- const { container } = render(Component, {
+ const { container } = render(TestComponent, {
props: {
defer: true,
},
@@ -215,8 +231,7 @@ describe('OverlayScrollbarsComponent', () => {
document.body.remove();
- const { unmount } = render(ComponentBody, {
- // @ts-ignore
+ const { unmount } = render(TestComponentBody, {
target: htmlElement,
props: {
element: 'body',
@@ -233,8 +248,8 @@ describe('OverlayScrollbarsComponent', () => {
});
test('ref', () => {
- let osRef: Ref | undefined;
- const { container } = render(Component, {
+ let osRef: OverlayScrollbarsComponentRef | undefined;
+ const { container } = render(TestComponent, {
props: {
getRef: (ref: any) => {
osRef = ref;
@@ -252,8 +267,8 @@ describe('OverlayScrollbarsComponent', () => {
});
test('options', async () => {
- let osRef: Ref | undefined;
- render(Component, {
+ let osRef: OverlayScrollbarsComponentRef | undefined;
+ render(TestComponent, {
props: {
options: { paddingAbsolute: true, overflow: { y: 'hidden' } },
getRef: (ref: any) => {
@@ -323,8 +338,8 @@ describe('OverlayScrollbarsComponent', () => {
const onInitialized = vi.fn();
const onUpdated = vi.fn();
const onUpdated2 = vi.fn();
- let osRef: Ref | undefined;
- render(Component, {
+ let osRef: OverlayScrollbarsComponentRef | undefined;
+ render(TestComponent, {
props: {
events: { initialized: onInitialized },
getRef: (ref: any) => {
@@ -409,8 +424,8 @@ describe('OverlayScrollbarsComponent', () => {
});
test('destroy', () => {
- let osRef: Ref | undefined;
- const { unmount } = render(Component, {
+ let osRef: OverlayScrollbarsComponentRef | undefined;
+ const { unmount } = render(TestComponent, {
props: {
getRef(ref: any) {
osRef = ref;
@@ -445,7 +460,7 @@ describe('OverlayScrollbarsComponent', () => {
const args = e.detail;
expect(args).toEqual([expect.any(Object), expect.any(Event)]);
});
- const { container, unmount } = render(Component, {
+ const { container, unmount } = render(TestComponent, {
props: {
initialized,
updated,
diff --git a/packages/overlayscrollbars-svelte/test/dom/TestComponent.svelte b/packages/overlayscrollbars-svelte/test/dom/TestComponent.svelte
new file mode 100644
index 00000000..767e397c
--- /dev/null
+++ b/packages/overlayscrollbars-svelte/test/dom/TestComponent.svelte
@@ -0,0 +1,93 @@
+
+
+
+ hello svelte
+ {#if childrenCount === 0}empty
{/if}
+ {#each [...Array(childrenCount).keys()] as child}
+
+ {/each}
+
+
+
+ props
diff --git a/packages/overlayscrollbars-svelte/test/dom/ComponentBody.svelte b/packages/overlayscrollbars-svelte/test/dom/TestComponentBody.svelte
similarity index 81%
rename from packages/overlayscrollbars-svelte/test/dom/ComponentBody.svelte
rename to packages/overlayscrollbars-svelte/test/dom/TestComponentBody.svelte
index abc8d418..a580a2cf 100644
--- a/packages/overlayscrollbars-svelte/test/dom/ComponentBody.svelte
+++ b/packages/overlayscrollbars-svelte/test/dom/TestComponentBody.svelte
@@ -1,6 +1,7 @@
diff --git a/packages/overlayscrollbars-svelte/test/dom/TestElement.svelte b/packages/overlayscrollbars-svelte/test/dom/TestElement.svelte
new file mode 100644
index 00000000..ed5543ab
--- /dev/null
+++ b/packages/overlayscrollbars-svelte/test/dom/TestElement.svelte
@@ -0,0 +1,10 @@
+
+
+
+ {@render children?.()}
+
diff --git a/packages/overlayscrollbars-svelte/test/dom/TestUseOverlayScrollbarsInitialize.svelte b/packages/overlayscrollbars-svelte/test/dom/TestUseOverlayScrollbarsInitialize.svelte
new file mode 100644
index 00000000..5e4e2669
--- /dev/null
+++ b/packages/overlayscrollbars-svelte/test/dom/TestUseOverlayScrollbarsInitialize.svelte
@@ -0,0 +1,29 @@
+
+
+
diff --git a/packages/overlayscrollbars-svelte/test/dom/useOverlayScrollbars.test.ts b/packages/overlayscrollbars-svelte/test/dom/useOverlayScrollbars.test.ts
new file mode 100644
index 00000000..0bc4e5bc
--- /dev/null
+++ b/packages/overlayscrollbars-svelte/test/dom/useOverlayScrollbars.test.ts
@@ -0,0 +1,49 @@
+import { describe, test, afterEach, expect } from 'vitest';
+import { cleanup, render, screen } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
+import { OverlayScrollbars } from 'overlayscrollbars';
+import type {
+ UseOverlayScrollbarsInitialization,
+ UseOverlayScrollbarsInstance,
+} from '../../src/useOverlayScrollbars.svelte';
+import TestUseOverlayScrollbarsInitialize from './TestUseOverlayScrollbarsInitialize.svelte';
+
+describe('useOverlayScrollbars', () => {
+ afterEach(() => cleanup());
+
+ test('re-initialization', async () => {
+ const { unmount } = render(TestUseOverlayScrollbarsInitialize, {
+ // @ts-ignore
+ props: {
+ click: (
+ initialize: UseOverlayScrollbarsInitialization,
+ instance: UseOverlayScrollbarsInstance,
+ instanceRef: { current: OverlayScrollbars | null },
+ target: HTMLElement
+ ) => {
+ initialize(target);
+ if (instanceRef.current) {
+ expect(instanceRef.current).toBe(instance());
+ }
+ instanceRef.current = instance();
+ expect(instanceRef.current).toBe(instance());
+ },
+ },
+ });
+
+ const initializeBtn = screen.getByRole('button');
+ await userEvent.click(initializeBtn);
+ // taking snapshot here wouldn't be equal because of "tabindex" attribute of the viewport element
+ await userEvent.click(initializeBtn);
+ const snapshot = initializeBtn.innerHTML;
+ await userEvent.click(initializeBtn);
+
+ expect(snapshot).toBe(initializeBtn.innerHTML);
+
+ expect(OverlayScrollbars(initializeBtn)).toBeDefined();
+
+ unmount();
+
+ expect(OverlayScrollbars(initializeBtn)).toBeUndefined();
+ });
+});
diff --git a/packages/overlayscrollbars-svelte/vite.config.js b/packages/overlayscrollbars-svelte/vite.config.js
index 6d43a8e1..cd86dd9d 100644
--- a/packages/overlayscrollbars-svelte/vite.config.js
+++ b/packages/overlayscrollbars-svelte/vite.config.js
@@ -1,6 +1,7 @@
import { resolve } from 'path';
-import { defineConfig } from 'vitest/config';
+import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
+import { svelteTesting } from '@testing-library/svelte/vite';
import { rollupPackageJsonPlugin } from '@~local/rollup/plugin/rollupPackageJsonPlugin';
import { rollupCopyPlugin } from '@~local/rollup/plugin/rollupCopyPlugin';
@@ -92,5 +93,5 @@ export default defineConfig({
],
},
},
- plugins: [svelte(process.env.VITEST ? { hot: false } : {})],
+ plugins: [svelte(process.env.VITEST ? { hot: false } : {}), svelteTesting()],
});
diff --git a/packages/overlayscrollbars-vue/src/OverlayScrollbarsComponent.vue b/packages/overlayscrollbars-vue/src/OverlayScrollbarsComponent.vue
index e351a9b0..aacb687e 100644
--- a/packages/overlayscrollbars-vue/src/OverlayScrollbarsComponent.vue
+++ b/packages/overlayscrollbars-vue/src/OverlayScrollbarsComponent.vue
@@ -1,14 +1,5 @@