fbredius/storybook

View on GitHub
lib/components/src/ScrollArea/OverlayScrollbars.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import React, { useRef, useEffect, HTMLAttributes, FC } from 'react';
import OverlayScrollbars from 'overlayscrollbars';

interface OverlayScrollbarsComponentProps extends HTMLAttributes<HTMLDivElement> {
  children?: any;
  options?: OverlayScrollbars.Options;
  extensions?: OverlayScrollbars.Extensions;
}

/**
 * Using overlayscrollbars-react component results use the esm modules
 * which doesn't go through babel leading to IE 11 uncompatibility
 * A PR is submitted that may fix this:
 * https://github.com/KingSora/OverlayScrollbars/pull/218
 * */

export const OverlayScrollbarsComponent: FC<OverlayScrollbarsComponentProps> = ({
  options = {},
  extensions,
  className,
  children,
  ...rest
}) => {
  const osTargetRef = useRef<HTMLDivElement>();
  const osInstance = useRef<OverlayScrollbars>();

  useEffect(() => {
    osInstance.current = OverlayScrollbars(osTargetRef.current, options, extensions);
    mergeHostClassNames(osInstance.current, className);
    return () => {
      if (OverlayScrollbars.valid(osInstance.current)) {
        osInstance.current.destroy();
        osInstance.current = null;
      }
    };
  }, []);

  useEffect(() => {
    if (OverlayScrollbars.valid(osInstance.current)) {
      osInstance.current.options(options);
    }
  }, [options]);

  useEffect(() => {
    if (OverlayScrollbars.valid(osInstance.current)) {
      mergeHostClassNames(osInstance.current, className);
    }
  }, [className]);

  return (
    <div className="os-host" {...rest} ref={osTargetRef}>
      <div className="os-resize-observer-host" />
      <div className="os-padding">
        <div className="os-viewport">
          <div className="os-content">{children}</div>
        </div>
      </div>
      <div className="os-scrollbar os-scrollbar-horizontal ">
        <div className="os-scrollbar-track">
          <div className="os-scrollbar-handle" />
        </div>
      </div>
      <div className="os-scrollbar os-scrollbar-vertical">
        <div className="os-scrollbar-track">
          <div className="os-scrollbar-handle" />
        </div>
      </div>
      <div className="os-scrollbar-corner" />
    </div>
  );
};

function mergeHostClassNames(osInstance: OverlayScrollbars, className: string) {
  if (OverlayScrollbars.valid(osInstance)) {
    const { host } = osInstance.getElements();
    const regex = new RegExp(
      `(^os-host([-_].+|)$)|${osInstance.options().className.replace(/\s/g, '$|')}$`,
      'g'
    );
    const osClassNames = host.className
      .split(' ')
      .filter((name) => name.match(regex))
      .join(' ');

    host.className = `${osClassNames} ${className || ''}`;
  }
}

export default OverlayScrollbarsComponent;