From cd07c743b604209555118f004b77140041e76d20 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 16 Jul 2024 13:16:13 +0430 Subject: [PATCH] Implement OverlayScrollbars throughout the app for MacOS-like scrolling experience (#5423) * feat: build overlay scrollbar component for Virtuoso elements * feat: apply overlay scroll to Virtuoso components * feat: build overlay scrollbar component for normal scrollable sections * feat: apply overlay scrollbar to normal scrollable sections * feat: add dark mode UI support to overlay scrollbars * chore: rename OverlayScrollbar to OverlayScrollbarForTypicalChildren * chore: move inline style to scss file * chore: rename VirtuosoOverlayScrollbar to OverlayScrollbarForVirtuosoChildren * chore: move OverlayScrollbarForTypicalChildren to components folder * chore: create a common component for handling Virtuoso and Typical scroll sections * chore: rename Virtuoso and Typical Overlay Scrollbar components * fix: fix the overlay scrollbar initialization flickering * fix: remove calculated height from typical overlay scrollbar + remove the explicit height: 100% --- frontend/package.json | 2 + .../OverlayScrollbar/OverlayScrollbar.tsx | 54 +++++++++ .../TypicalOverlayScrollbar.tsx | 31 +++++ .../typicalOverlayScrollbar.scss | 3 + .../VirtuosoOverlayScrollbar.tsx | 37 ++++++ .../virtuosoOverlayScrollbar.scss | 5 + .../container/AppLayout/AppLayout.styles.scss | 1 - frontend/src/container/AppLayout/index.tsx | 40 ++++--- frontend/src/container/AppLayout/styles.ts | 1 - .../GridCardLayout/GridCardLayout.styles.scss | 1 + .../GridCardLayout/GridCardLayout.tsx | 6 +- .../container/LiveLogs/LiveLogsList/index.tsx | 17 +-- .../ContextView/ContextLogRenderer.tsx | 17 +-- .../src/container/LogsContextList/index.tsx | 18 +-- .../src/container/LogsExplorerList/index.tsx | 21 ++-- .../LogsPanelTable/LogsPanelComponent.tsx | 25 ++-- frontend/src/container/LogsTable/index.tsx | 5 +- .../DashboardDescription/SettingsDrawer.tsx | 5 +- frontend/src/container/NewDashboard/index.tsx | 2 +- frontend/src/container/NewWidget/index.tsx | 107 +++++++++--------- .../TracesTableComponent.tsx | 25 ++-- .../useInitializeOverlayScrollbar.tsx | 43 +++++++ frontend/src/styles.scss | 1 + frontend/yarn.lock | 12 +- 24 files changed, 351 insertions(+), 128 deletions(-) create mode 100644 frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx create mode 100644 frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx create mode 100644 frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss create mode 100644 frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx create mode 100644 frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss create mode 100644 frontend/src/hooks/useInitializeOverlayScrollbar/useInitializeOverlayScrollbar.tsx diff --git a/frontend/package.json b/frontend/package.json index 2b2e803478..34e08ea263 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -110,6 +110,8 @@ "react-syntax-highlighter": "15.5.0", "react-use": "^17.3.2", "react-virtuoso": "4.0.3", + "overlayscrollbars-react": "^0.5.6", + "overlayscrollbars": "^2.8.1", "redux": "^4.0.5", "redux-thunk": "^2.3.0", "rehype-raw": "7.0.0", diff --git a/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx b/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx new file mode 100644 index 0000000000..73c95ce27d --- /dev/null +++ b/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx @@ -0,0 +1,54 @@ +import TypicalOverlayScrollbar from 'components/TypicalOverlayScrollbar/TypicalOverlayScrollbar'; +import VirtuosoOverlayScrollbar from 'components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { PartialOptions } from 'overlayscrollbars'; +import { CSSProperties, ReactElement, useMemo } from 'react'; + +type Props = { + children: ReactElement; + isVirtuoso?: boolean; + style?: CSSProperties; + options?: PartialOptions; +}; + +function OverlayScrollbar({ + children, + isVirtuoso, + style, + options: customOptions, +}: Props): any { + const isDarkMode = useIsDarkMode(); + const options = useMemo( + () => + ({ + scrollbars: { + autoHide: 'scroll', + theme: isDarkMode ? 'os-theme-light' : 'os-theme-dark', + }, + ...(customOptions || {}), + } as PartialOptions), + [customOptions, isDarkMode], + ); + + if (isVirtuoso) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} + +OverlayScrollbar.defaultProps = { + isVirtuoso: false, + style: {}, + options: {}, +}; + +export default OverlayScrollbar; diff --git a/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx b/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx new file mode 100644 index 0000000000..1ed1717d6d --- /dev/null +++ b/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx @@ -0,0 +1,31 @@ +import './typicalOverlayScrollbar.scss'; + +import { PartialOptions } from 'overlayscrollbars'; +import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; +import { CSSProperties, ReactElement } from 'react'; + +interface Props { + children: ReactElement; + style?: CSSProperties; + options?: PartialOptions; +} + +export default function TypicalOverlayScrollbar({ + children, + style, + options, +}: Props): ReturnType { + return ( + + {children} + + ); +} + +TypicalOverlayScrollbar.defaultProps = { style: {}, options: {} }; diff --git a/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss b/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss new file mode 100644 index 0000000000..9dd4c3b381 --- /dev/null +++ b/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss @@ -0,0 +1,3 @@ +.overlay-scrollbar { + height: 100%; +} diff --git a/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx b/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx new file mode 100644 index 0000000000..4f0e905fc2 --- /dev/null +++ b/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx @@ -0,0 +1,37 @@ +import './virtuosoOverlayScrollbar.scss'; + +import useInitializeOverlayScrollbar from 'hooks/useInitializeOverlayScrollbar/useInitializeOverlayScrollbar'; +import { PartialOptions } from 'overlayscrollbars'; +import React, { CSSProperties, ReactElement } from 'react'; + +interface VirtuosoOverlayScrollbarProps { + children: ReactElement; + style?: CSSProperties; + options: PartialOptions; +} + +export default function VirtuosoOverlayScrollbar({ + children, + style, + options, +}: VirtuosoOverlayScrollbarProps): JSX.Element { + const { rootRef, setScroller } = useInitializeOverlayScrollbar(options); + + const enhancedChild = React.cloneElement(children, { + scrollerRef: setScroller, + 'data-overlayscrollbars-initialize': true, + }); + + return ( +
+ {enhancedChild} +
+ ); +} + +VirtuosoOverlayScrollbar.defaultProps = { style: {} }; diff --git a/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss b/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss new file mode 100644 index 0000000000..728bd1e8c3 --- /dev/null +++ b/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss @@ -0,0 +1,5 @@ +.overlay-scroll-wrapper { + height: 100%; + width: 100%; + overflow: auto; +} diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index 6a0b223edf..c789ba5e0f 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -5,7 +5,6 @@ .app-content { width: calc(100% - 64px); - overflow: auto; z-index: 0; .content-container { diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 774e0e888d..ba64e9af45 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -9,6 +9,7 @@ import getLocalStorageKey from 'api/browser/localstorage/get'; import getUserLatestVersion from 'api/user/getLatestVersion'; import getUserVersion from 'api/user/getVersion'; import cx from 'classnames'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import ROUTES from 'constants/routes'; import SideNav from 'container/SideNav'; @@ -303,24 +304,29 @@ function AppLayout(props: AppLayoutProps): JSX.Element { collapsed={collapsed} /> )} -
+
}> - - - {isToDisplayLayout && !renderFullScreen && } - {children} - + + + + {isToDisplayLayout && !renderFullScreen && } + {children} + +
diff --git a/frontend/src/container/AppLayout/styles.ts b/frontend/src/container/AppLayout/styles.ts index 5edddcac40..c66d2ee4d8 100644 --- a/frontend/src/container/AppLayout/styles.ts +++ b/frontend/src/container/AppLayout/styles.ts @@ -13,7 +13,6 @@ export const Layout = styled(LayoutComponent)` `; export const LayoutContent = styled(LayoutComponent.Content)` - overflow-y: auto; height: 100%; &::-webkit-scrollbar { width: 0.1rem; diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss index 77e3a0822e..1b2bd2a7ba 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss +++ b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss @@ -1,6 +1,7 @@ .fullscreen-grid-container { overflow: auto; margin: 8px -8px; + margin-right: 0; .react-grid-layout { border: none !important; diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index 11bdf492ee..f0d178866c 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -428,7 +428,11 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { return isDashboardEmpty ? ( ) : ( - + ) : ( - + + + )} diff --git a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx index f80e706d30..d5c9f68547 100644 --- a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx +++ b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx @@ -2,6 +2,7 @@ import './ContextLogRenderer.styles.scss'; import { Skeleton } from 'antd'; import RawLogView from 'components/Logs/RawLogView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import ShowButton from 'container/LogsContextList/ShowButton'; import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; import { useCallback, useEffect, useState } from 'react'; @@ -94,13 +95,15 @@ function ContextLogRenderer({ }} /> )} - + + + {isAfterLogsFetching && ( No Data )} {isFetching && } - - + + + {order === ORDERBY_FILTERS.DESC && ( diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx index fc5a1f6800..0727d7100f 100644 --- a/frontend/src/container/LogsExplorerList/index.tsx +++ b/frontend/src/container/LogsExplorerList/index.tsx @@ -6,6 +6,7 @@ import { VIEW_TYPES } from 'components/LogDetail/constants'; // components import ListLogView from 'components/Logs/ListLogView'; import RawLogView from 'components/Logs/RawLogView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import Spinner from 'components/Spinner'; import { CARD_BODY_STYLE } from 'constants/card'; import { LOCALSTORAGE } from 'constants/localStorage'; @@ -133,15 +134,17 @@ function LogsExplorerList({ style={{ width: '100%', marginTop: '20px' }} bodyStyle={CARD_BODY_STYLE} > - + + + ); }, [ diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index f1d6e3642c..fef68ee60f 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -3,6 +3,7 @@ import './LogsPanelComponent.styles.scss'; import { Table } from 'antd'; import LogDetail from 'components/LogDetail'; import { VIEW_TYPES } from 'components/LogDetail/constants'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Controls from 'container/Controls'; @@ -207,17 +208,19 @@ function LogsPanelComponent({ <>
- + +
+ {!widget.query.builder.queryData[0].limit && (
diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx index b10c3503dd..6b20986d1f 100644 --- a/frontend/src/container/LogsTable/index.tsx +++ b/frontend/src/container/LogsTable/index.tsx @@ -7,6 +7,7 @@ import { VIEW_TYPES } from 'components/LogDetail/constants'; import ListLogView from 'components/Logs/ListLogView'; import RawLogView from 'components/Logs/RawLogView'; import LogsTableView from 'components/Logs/TableView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import Spinner from 'components/Spinner'; import { CARD_BODY_STYLE } from 'constants/card'; import { useActiveLog } from 'hooks/logs/useActiveLog'; @@ -97,7 +98,9 @@ function LogsTable(props: LogsTableProps): JSX.Element { return ( - + + + ); }, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]); diff --git a/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx index bbf0b12b45..e06332e562 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx @@ -2,6 +2,7 @@ import './Description.styles.scss'; import { Button } from 'antd'; import ConfigureIcon from 'assets/Integrations/ConfigureIcon'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { useRef, useState } from 'react'; import DashboardSettingsContent from '../DashboardSettings'; @@ -41,7 +42,9 @@ function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element { open={visible} rootClassName="settings-container-root" > - + + + ); diff --git a/frontend/src/container/NewDashboard/index.tsx b/frontend/src/container/NewDashboard/index.tsx index 2042d77e16..d5daf1c3cb 100644 --- a/frontend/src/container/NewDashboard/index.tsx +++ b/frontend/src/container/NewDashboard/index.tsx @@ -6,7 +6,7 @@ import GridGraphs from './GridGraphs'; function NewDashboard(): JSX.Element { const handle = useFullScreenHandle(); return ( -
+
diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index fc0c5d06cd..811855b26a 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -5,6 +5,7 @@ import { WarningOutlined } from '@ant-design/icons'; import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { chartHelpMessage } from 'components/facingIssueBtn/util'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { FeatureKeys } from 'constants/features'; import { QueryParams } from 'constants/query'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; @@ -586,60 +587,64 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { - {selectedWidget && ( - - )} + + {selectedWidget && ( + + )} + - + + +
-
+ +
+
>; + rootRef: RefObject; +} => { + const rootRef = useRef(null); + const [scroller, setScroller] = useState(null); + const [initialize, osInstance] = useOverlayScrollbars({ + defer: true, + options, + }); + + useEffect(() => { + const { current: root } = rootRef; + + if (scroller && root) { + initialize({ + target: root, + elements: { + viewport: scroller, + }, + }); + } + + return (): void => osInstance()?.destroy(); + }, [scroller, initialize, osInstance]); + + return { setScroller, rootRef }; +}; + +export default useInitializeOverlayScrollbar; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index b5aca15ddf..7e50e4e38c 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -1,4 +1,5 @@ @import '@signozhq/design-tokens/dist/style.css'; +@import 'overlayscrollbars/overlayscrollbars.css'; @import './periscope.scss'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 73bf7ae208..78c76e6be5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -13028,6 +13028,16 @@ outvariant@^1.2.1, outvariant@^1.4.0: resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.0.tgz#e742e4bda77692da3eca698ef5bfac62d9fba06e" integrity sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw== +overlayscrollbars-react@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/overlayscrollbars-react/-/overlayscrollbars-react-0.5.6.tgz#e9779f9fc2c1a3288570a45c83f8e42518bfb8c1" + integrity sha512-E5To04bL5brn9GVCZ36SnfGanxa2I2MDkWoa4Cjo5wol7l+diAgi4DBc983V7l2nOk/OLJ6Feg4kySspQEGDBw== + +overlayscrollbars@^2.8.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/overlayscrollbars/-/overlayscrollbars-2.9.2.tgz#056020a3811742b58b754fab6f775d49bd109be9" + integrity sha512-iDT84r39i7oWP72diZN2mbJUsn/taCq568aQaIrc84S87PunBT7qtsVltAF2esk7ORTRjQDnfjVYoqqTzgs8QA== + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -14678,7 +14688,7 @@ react-use@17.4.0, react-use@^17.3.2: react-virtuoso@4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.0.3.tgz#0dc8b10978095852d985b064157639b9fb9d9b1e" integrity sha512-tyqt8FBWxO+smve/kUgJbhCI2MEOvH2hHgFYPKWBMA2cJmV+cHIDDh1BL/6w4pg/dcCdlHCNVwi6aiztPxWttw== react@18.2.0: