mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:39:04 +08:00
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%
This commit is contained in:
parent
46e6c34e51
commit
cd07c743b6
@ -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",
|
||||
|
@ -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 (
|
||||
<VirtuosoOverlayScrollbar style={style} options={options}>
|
||||
{children}
|
||||
</VirtuosoOverlayScrollbar>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TypicalOverlayScrollbar style={style} options={options}>
|
||||
{children}
|
||||
</TypicalOverlayScrollbar>
|
||||
);
|
||||
}
|
||||
|
||||
OverlayScrollbar.defaultProps = {
|
||||
isVirtuoso: false,
|
||||
style: {},
|
||||
options: {},
|
||||
};
|
||||
|
||||
export default OverlayScrollbar;
|
@ -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<typeof OverlayScrollbarsComponent> {
|
||||
return (
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
options={options}
|
||||
style={style}
|
||||
className="overlay-scrollbar"
|
||||
data-overlayscrollbars-initialize
|
||||
>
|
||||
{children}
|
||||
</OverlayScrollbarsComponent>
|
||||
);
|
||||
}
|
||||
|
||||
TypicalOverlayScrollbar.defaultProps = { style: {}, options: {} };
|
@ -0,0 +1,3 @@
|
||||
.overlay-scrollbar {
|
||||
height: 100%;
|
||||
}
|
@ -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 (
|
||||
<div
|
||||
data-overlayscrollbars-initialize
|
||||
ref={rootRef}
|
||||
className="overlay-scroll-wrapper"
|
||||
style={style}
|
||||
>
|
||||
{enhancedChild}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
VirtuosoOverlayScrollbar.defaultProps = { style: {} };
|
@ -0,0 +1,5 @@
|
||||
.overlay-scroll-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
|
||||
.app-content {
|
||||
width: calc(100% - 64px);
|
||||
overflow: auto;
|
||||
z-index: 0;
|
||||
|
||||
.content-container {
|
||||
|
@ -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,9 +304,13 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
collapsed={collapsed}
|
||||
/>
|
||||
)}
|
||||
<div className={cx('app-content', collapsed ? 'collapsed' : '')}>
|
||||
<div
|
||||
className={cx('app-content', collapsed ? 'collapsed' : '')}
|
||||
data-overlayscrollbars-initialize
|
||||
>
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
<LayoutContent>
|
||||
<LayoutContent data-overlayscrollbars-initialize>
|
||||
<OverlayScrollbar>
|
||||
<ChildrenContainer
|
||||
style={{
|
||||
margin:
|
||||
@ -321,6 +326,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
{isToDisplayLayout && !renderFullScreen && <TopNav />}
|
||||
{children}
|
||||
</ChildrenContainer>
|
||||
</OverlayScrollbar>
|
||||
</LayoutContent>
|
||||
</Sentry.ErrorBoundary>
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
.fullscreen-grid-container {
|
||||
overflow: auto;
|
||||
margin: 8px -8px;
|
||||
margin-right: 0;
|
||||
|
||||
.react-grid-layout {
|
||||
border: none !important;
|
||||
|
@ -428,7 +428,11 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
return isDashboardEmpty ? (
|
||||
<DashboardEmptyState />
|
||||
) : (
|
||||
<FullScreen handle={handle} className="fullscreen-grid-container">
|
||||
<FullScreen
|
||||
handle={handle}
|
||||
className="fullscreen-grid-container"
|
||||
data-overlayscrollbars-initialize
|
||||
>
|
||||
<ReactGridLayout
|
||||
cols={12}
|
||||
rowHeight={45}
|
||||
|
@ -3,6 +3,7 @@ import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
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';
|
||||
@ -128,6 +129,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
|
||||
/>
|
||||
) : (
|
||||
<Card style={{ width: '100%' }} bodyStyle={CARD_BODY_STYLE}>
|
||||
<OverlayScrollbar isVirtuoso>
|
||||
<Virtuoso
|
||||
ref={ref}
|
||||
initialTopMostItemIndex={activeLogIndex !== -1 ? activeLogIndex : 0}
|
||||
@ -135,6 +137,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
|
||||
totalCount={logs.length}
|
||||
itemContent={getItemContent}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</Card>
|
||||
)}
|
||||
</InfinityWrapperStyled>
|
||||
|
@ -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,6 +95,7 @@ function ContextLogRenderer({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<OverlayScrollbar isVirtuoso>
|
||||
<Virtuoso
|
||||
className="virtuoso-list"
|
||||
initialTopMostItemIndex={0}
|
||||
@ -101,6 +103,7 @@ function ContextLogRenderer({
|
||||
itemContent={getItemContent}
|
||||
style={{ height: `calc(${logs.length} * 32px)` }}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
{isAfterLogsFetching && (
|
||||
<Skeleton
|
||||
style={{
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './LogsContextList.styles.scss';
|
||||
|
||||
import RawLogView from 'components/Logs/RawLogView';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@ -187,7 +188,7 @@ function LogsContextList({
|
||||
<EmptyText>No Data</EmptyText>
|
||||
)}
|
||||
{isFetching && <Spinner size="large" height="10rem" />}
|
||||
|
||||
<OverlayScrollbar isVirtuoso>
|
||||
<Virtuoso
|
||||
className="virtuoso-list"
|
||||
initialTopMostItemIndex={0}
|
||||
@ -195,6 +196,7 @@ function LogsContextList({
|
||||
itemContent={getItemContent}
|
||||
followOutput={order === ORDERBY_FILTERS.DESC}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</ListContainer>
|
||||
|
||||
{order === ORDERBY_FILTERS.DESC && (
|
||||
|
@ -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,6 +134,7 @@ function LogsExplorerList({
|
||||
style={{ width: '100%', marginTop: '20px' }}
|
||||
bodyStyle={CARD_BODY_STYLE}
|
||||
>
|
||||
<OverlayScrollbar isVirtuoso>
|
||||
<Virtuoso
|
||||
ref={ref}
|
||||
initialTopMostItemIndex={activeLogIndex !== -1 ? activeLogIndex : 0}
|
||||
@ -142,6 +144,7 @@ function LogsExplorerList({
|
||||
itemContent={getItemContent}
|
||||
components={components}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</Card>
|
||||
);
|
||||
}, [
|
||||
|
@ -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,6 +208,7 @@ function LogsPanelComponent({
|
||||
<>
|
||||
<div className="logs-table">
|
||||
<div className="resize-table">
|
||||
<OverlayScrollbar>
|
||||
<Table
|
||||
pagination={false}
|
||||
tableLayout="fixed"
|
||||
@ -218,6 +220,7 @@ function LogsPanelComponent({
|
||||
columns={columns}
|
||||
onRow={handleRow}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</div>
|
||||
{!widget.query.builder.queryData[0].limit && (
|
||||
<div className="controller">
|
||||
|
@ -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 (
|
||||
<Card className="logs-card" bodyStyle={CARD_BODY_STYLE}>
|
||||
<OverlayScrollbar isVirtuoso>
|
||||
<Virtuoso totalCount={logs.length} itemContent={getItemContent} />
|
||||
</OverlayScrollbar>
|
||||
</Card>
|
||||
);
|
||||
}, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]);
|
||||
|
@ -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"
|
||||
>
|
||||
<OverlayScrollbar>
|
||||
<DashboardSettingsContent variableViewModeRef={variableViewModeRef} />
|
||||
</OverlayScrollbar>
|
||||
</DrawerContainer>
|
||||
</>
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import GridGraphs from './GridGraphs';
|
||||
function NewDashboard(): JSX.Element {
|
||||
const handle = useFullScreenHandle();
|
||||
return (
|
||||
<div style={{ overflowX: 'hidden' }}>
|
||||
<div>
|
||||
<Description handle={handle} />
|
||||
<GridGraphs handle={handle} />
|
||||
</div>
|
||||
|
@ -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,6 +587,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
|
||||
<PanelContainer>
|
||||
<LeftContainerWrapper isDarkMode={useIsDarkMode()}>
|
||||
<OverlayScrollbar>
|
||||
{selectedWidget && (
|
||||
<LeftContainer
|
||||
selectedGraph={graphType}
|
||||
@ -600,9 +602,11 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
isLoadingPanelData={isLoadingPanelData}
|
||||
/>
|
||||
)}
|
||||
</OverlayScrollbar>
|
||||
</LeftContainerWrapper>
|
||||
|
||||
<RightContainerWrapper>
|
||||
<OverlayScrollbar>
|
||||
<RightContainer
|
||||
setGraphHandler={setGraphHandler}
|
||||
title={title}
|
||||
@ -640,6 +644,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
softMax={softMax}
|
||||
setSoftMax={setSoftMax}
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</RightContainerWrapper>
|
||||
</PanelContainer>
|
||||
<Modal
|
||||
|
@ -1,6 +1,7 @@
|
||||
import './TracesTableComponent.styles.scss';
|
||||
|
||||
import { Table } from 'antd';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import Controls from 'container/Controls';
|
||||
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
|
||||
@ -86,6 +87,7 @@ function TracesTableComponent({
|
||||
return (
|
||||
<div className="traces-table">
|
||||
<div className="resize-table">
|
||||
<OverlayScrollbar>
|
||||
<Table
|
||||
pagination={false}
|
||||
tableLayout="fixed"
|
||||
@ -97,6 +99,7 @@ function TracesTableComponent({
|
||||
onRow={handleRow}
|
||||
sticky
|
||||
/>
|
||||
</OverlayScrollbar>
|
||||
</div>
|
||||
<div className="controller">
|
||||
<Controls
|
||||
|
@ -0,0 +1,43 @@
|
||||
import { PartialOptions } from 'overlayscrollbars';
|
||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||
import {
|
||||
Dispatch,
|
||||
RefObject,
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
const useInitializeOverlayScrollbar = (
|
||||
options: PartialOptions,
|
||||
): {
|
||||
setScroller: Dispatch<SetStateAction<null>>;
|
||||
rootRef: RefObject<HTMLDivElement>;
|
||||
} => {
|
||||
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;
|
@ -1,4 +1,5 @@
|
||||
@import '@signozhq/design-tokens/dist/style.css';
|
||||
@import 'overlayscrollbars/overlayscrollbars.css';
|
||||
|
||||
@import './periscope.scss';
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user