finish overlayscrollbars-react

This commit is contained in:
Rene Haas
2022-11-01 13:25:44 +01:00
parent fe6262939e
commit 3c882f5475
7 changed files with 197 additions and 31 deletions
@@ -1,17 +1,23 @@
import { forwardRef, useEffect, useRef, useImperativeHandle } from 'react';
import { OverlayScrollbars } from 'overlayscrollbars';
import type { OverlayScrollbars } from 'overlayscrollbars';
import type { PartialOptions, EventListeners } from 'overlayscrollbars';
import type { ComponentPropsWithoutRef, ElementRef, ForwardedRef } from 'react';
import { useOverlayScrollbars } from './useOverlayScrollbars';
export type OverlayScrollbarsComponentProps<T extends keyof JSX.IntrinsicElements = 'div'> =
ComponentPropsWithoutRef<T> & {
/** Tag of the root element. */
element?: T;
/** OverlayScrollbars options. */
options?: PartialOptions;
/** OverlayScrollbars events. */
events?: EventListeners;
};
export interface OverlayScrollbarsComponentRef<T extends keyof JSX.IntrinsicElements = 'div'> {
/** Returns the OverlayScrollbars instance or null if not initialized. */
instance(): OverlayScrollbars | null;
/** Returns the target element. */
target(): ElementRef<T> | null;
}
@@ -22,50 +28,31 @@ const OverlayScrollbarsComponent = <T extends keyof JSX.IntrinsicElements>(
const { element = 'div', options, events, children, ...other } = props;
const Tag = element;
const [initialize, instance] = useOverlayScrollbars(options, events);
const osTargetRef = useRef<ElementRef<T>>(null);
const osChildrenRef = useRef<HTMLDivElement>(null);
const osInstanceRef = useRef<OverlayScrollbars | null>(null);
useEffect(() => {
const { current: targetElm } = osTargetRef;
const { current: childrenElm } = osChildrenRef;
if (targetElm && childrenElm) {
const instance = OverlayScrollbars(
{
target: targetElm as any,
elements: {
viewport: childrenElm,
content: childrenElm,
},
const osInstance = initialize({
target: targetElm as any,
elements: {
viewport: childrenElm,
content: childrenElm,
},
options || {},
events
);
osInstanceRef.current = instance;
});
return () => instance.destroy();
return () => osInstance.destroy();
}
}, []);
useEffect(() => {
const { current: instance } = osInstanceRef;
if (OverlayScrollbars.valid(instance) && options) {
instance.options(options, true);
}
}, [options]);
useEffect(() => {
const { current: instance } = osInstanceRef;
if (OverlayScrollbars.valid(instance) && events) {
return instance.on(events);
}
}, [events]);
useImperativeHandle(
ref,
() => {
return {
instance: () => osInstanceRef.current,
instance,
target: () => osTargetRef.current,
};
},
@@ -1 +1,2 @@
export * from './OverlayScrollbarsComponent';
export * from './useOverlayScrollbars';
@@ -0,0 +1,69 @@
import { useEffect, useCallback, useRef } from 'react';
import { OverlayScrollbars } from 'overlayscrollbars';
import type { PartialOptions, InitializationTarget, EventListeners } from 'overlayscrollbars';
export type UseOverlayScrollbarsInitialization = (
target: InitializationTarget
) => OverlayScrollbars;
export type UseOverlayScrollbarsInstance = () => OverlayScrollbars | null;
/**
* Hook for advanced usage of OverlayScrollbars. (When the OverlayScrollbarsComponent is not enough)
* @param options OverlayScrollbars options.
* @param events OverlayScrollbars events.
* @returns A tuple with two values:
* The first value is the initialization function.
* The second value is an function which returns the current OverlayScrollbars instance or null if not initialized.
*/
export const useOverlayScrollbars = (
options?: PartialOptions,
events?: EventListeners
): [UseOverlayScrollbarsInitialization, UseOverlayScrollbarsInstance] => {
const osInstanceRef = useRef<OverlayScrollbars | null>(null);
const optionsRef = useRef<PartialOptions>();
const eventsRef = useRef<EventListeners>();
const offInitialEventsRef = useRef<(() => void) | void>();
useEffect(() => {
const { current: instance } = osInstanceRef;
if (OverlayScrollbars.valid(instance) && options) {
instance.options(options, true);
}
}, [options]);
useEffect(() => {
const { current: instance } = osInstanceRef;
const { current: offInitialEvents } = offInitialEventsRef;
if (OverlayScrollbars.valid(instance) && events) {
offInitialEvents && (offInitialEventsRef.current = offInitialEvents()); // once called assign it to undefined so its not called again
return instance.on(events);
}
}, [events]);
optionsRef.current = options;
eventsRef.current = events;
return [
useCallback((target: InitializationTarget): OverlayScrollbars => {
// if already initialized return the current instance
const presentInstance = osInstanceRef.current;
if (OverlayScrollbars.valid(presentInstance)) {
return presentInstance;
}
const currOptions = optionsRef.current || {};
const currEvents = eventsRef.current || {};
const osInstance = (osInstanceRef.current = OverlayScrollbars(
target,
currOptions,
currEvents
));
offInitialEventsRef.current = osInstance.on(currEvents);
return osInstance;
}, []),
useCallback(() => osInstanceRef.current, []),
];
};