import React, { FC, useCallback, useEffect, useRef } from 'react'; import isNil from 'lodash/isNil'; import classNames from 'classnames'; import { css } from '@emotion/css'; import Scrollbars from 'react-custom-scrollbars'; import { useStyles } from '../../themes'; import { GrafanaTheme } from '@grafana/data'; interface Props { className?: string; autoHide?: boolean; autoHideTimeout?: number; autoHeightMax?: string; hideTracksWhenNotNeeded?: boolean; hideHorizontalTrack?: boolean; hideVerticalTrack?: boolean; scrollTop?: number; setScrollTop?: (event: any) => void; autoHeightMin?: number | string; updateAfterMountMs?: number; } /** * Wraps component into component from `react-custom-scrollbars` */ export const CustomScrollbar: FC = ({ autoHide = false, autoHideTimeout = 200, setScrollTop, className, autoHeightMin = '0', autoHeightMax = '100%', hideTracksWhenNotNeeded = false, hideHorizontalTrack, hideVerticalTrack, updateAfterMountMs, scrollTop, children, }) => { const ref = useRef(null); const styles = useStyles(getStyles); const updateScroll = () => { if (ref.current && !isNil(scrollTop)) { ref.current.scrollTop(scrollTop); } }; useEffect(() => { updateScroll(); }); /** * Special logic for doing a update a few milliseconds after mount to check for * updated height due to dynamic content */ useEffect(() => { if (!updateAfterMountMs) { return; } setTimeout(() => { const scrollbar = ref.current as any; if (scrollbar?.update) { scrollbar.update(); } }, updateAfterMountMs); }, [updateAfterMountMs]); function renderTrack(className: string, hideTrack: boolean | undefined, passedProps: any) { if (passedProps.style && hideTrack) { passedProps.style.display = 'none'; } return
; } const renderTrackHorizontal = useCallback( (passedProps: any) => { return renderTrack('track-horizontal', hideHorizontalTrack, passedProps); }, [hideHorizontalTrack] ); const renderTrackVertical = useCallback( (passedProps: any) => { return renderTrack('track-vertical', hideVerticalTrack, passedProps); }, [hideVerticalTrack] ); const renderThumbHorizontal = useCallback((passedProps: any) => { return
; }, []); const renderThumbVertical = useCallback((passedProps: any) => { return
; }, []); const renderView = useCallback((passedProps: any) => { return
; }, []); return ( {children} ); }; export default CustomScrollbar; const getStyles = (theme: GrafanaTheme) => { return { customScrollbar: css` // Fix for Firefox. For some reason sometimes .view container gets a height of its content, but in order to // make scroll working it should fit outer container size (scroll appears only when inner container size is // greater than outer one). display: flex; flex-grow: 1; .scrollbar-view { display: flex; flex-grow: 1; flex-direction: column; } .track-vertical { border-radius: ${theme.border.radius.md}; width: ${theme.spacing.sm} !important; right: 0px; bottom: ${theme.spacing.xxs}; top: ${theme.spacing.xxs}; } .track-horizontal { border-radius: ${theme.border.radius.md}; height: ${theme.spacing.sm} !important; right: ${theme.spacing.xxs}; bottom: ${theme.spacing.xxs}; left: ${theme.spacing.xxs}; } .thumb-vertical { background: ${theme.colors.bg3}; border-radius: ${theme.border.radius.md}; opacity: 0; } .thumb-horizontal { background: ${theme.colors.bg3}; border-radius: ${theme.border.radius.md}; opacity: 0; } &:hover { .thumb-vertical, .thumb-horizontal { opacity: 1; transition: opacity 0.3s ease-in-out; } } `, }; };