Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/components/Drawer/Drawer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.ydb-drawer {
&__drawer-container {
position: relative;

overflow: hidden;

height: 100%;
}

&__item {
z-index: 4;

height: 100%;
}
}
165 changes: 165 additions & 0 deletions src/components/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React from 'react';

import {DrawerItem, Drawer as GravityDrawer} from '@gravity-ui/navigation';

import {cn} from '../../utils/cn';
import {isNumeric} from '../../utils/utils';

import {useDrawerContext} from './DrawerContext';

const DEFAULT_DRAWER_WIDTH_PERCENTS = 60;
const DEFAULT_DRAWER_WIDTH = 600;
const DRAWER_WIDTH_KEY = 'drawer-width';
const b = cn('ydb-drawer');

import './Drawer.scss';

interface DrawerPaneContentWrapperProps {
isVisible: boolean;
onClose: () => void;
children: React.ReactNode;
drawerId?: string;
storageKey?: string;
direction?: 'left' | 'right';
className?: string;
detectClickOutside?: boolean;
defaultWidth?: number;
isPercentageWidth?: boolean;
}

const DrawerPaneContentWrapper = ({
isVisible,
onClose,
children,
drawerId = 'drawer',
storageKey = DRAWER_WIDTH_KEY,
defaultWidth,
direction = 'right',
className,
detectClickOutside = false,
isPercentageWidth,
}: DrawerPaneContentWrapperProps) => {
const [drawerWidth, setDrawerWidth] = React.useState(() => {
const savedWidth = localStorage.getItem(storageKey);
return isNumeric(savedWidth) ? Number(savedWidth) : defaultWidth;
});

const drawerRef = React.useRef<HTMLDivElement>(null);
const {containerWidth} = useDrawerContext();
// Calculate drawer width based on container width percentage if specified
const calculatedWidth = React.useMemo(() => {
if (isPercentageWidth && containerWidth > 0) {
return Math.round(
(containerWidth * (drawerWidth || DEFAULT_DRAWER_WIDTH_PERCENTS)) / 100,
);
}
return drawerWidth || DEFAULT_DRAWER_WIDTH;
}, [containerWidth, isPercentageWidth, drawerWidth]);

React.useEffect(() => {
if (!detectClickOutside) {
return undefined;
}

const handleClickOutside = (event: MouseEvent) => {
if (
isVisible &&
drawerRef.current &&
!drawerRef.current.contains(event.target as Node)
) {
onClose();
}
};

document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isVisible, onClose, detectClickOutside]);

const handleResizeDrawer = (width: number) => {
if (isPercentageWidth && containerWidth > 0) {
const percentageWidth = Math.round((width / containerWidth) * 100);
setDrawerWidth(percentageWidth);
localStorage.setItem(storageKey, percentageWidth.toString());
} else {
setDrawerWidth(width);
localStorage.setItem(storageKey, width.toString());
}
};

return (
<GravityDrawer
onEscape={onClose}
onVeilClick={onClose}
hideVeil
className={b('container', className)}
>
<DrawerItem
id={drawerId}
visible={isVisible}
resizable
maxResizeWidth={containerWidth}
width={isPercentageWidth ? calculatedWidth : drawerWidth}
onResize={handleResizeDrawer}
direction={direction}
className={b('item')}
ref={detectClickOutside ? drawerRef : undefined}
>
{children}
</DrawerItem>
</GravityDrawer>
);
};

interface DrawerPaneProps {
children: React.ReactNode;
renderDrawerContent: () => React.ReactNode;
isDrawerVisible: boolean;
onCloseDrawer: () => void;
drawerId?: string;
storageKey?: string;
defaultWidth?: number;
direction?: 'left' | 'right';
className?: string;
detectClickOutside?: boolean;
isPercentageWidth?: boolean;
}

export const DrawerWrapper = ({
children,
renderDrawerContent,
isDrawerVisible,
onCloseDrawer,
drawerId,
storageKey,
defaultWidth,
direction,
className,
detectClickOutside,
isPercentageWidth,
}: DrawerPaneProps) => {
React.useEffect(() => {
return () => {
onCloseDrawer();
};
}, [onCloseDrawer]);
return (
<React.Fragment>
{children}
<DrawerPaneContentWrapper
isVisible={isDrawerVisible}
onClose={onCloseDrawer}
drawerId={drawerId}
storageKey={storageKey}
defaultWidth={defaultWidth}
direction={direction}
className={className}
detectClickOutside={detectClickOutside}
isPercentageWidth={isPercentageWidth}
>
{renderDrawerContent()}
</DrawerPaneContentWrapper>
</React.Fragment>
);
};
79 changes: 79 additions & 0 deletions src/components/Drawer/DrawerContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';

import {cn} from '../../utils/cn';

import './Drawer.scss';

const b = cn('ydb-drawer');

export interface DrawerContextType {
containerWidth: number;
setContainerWidth: React.Dispatch<React.SetStateAction<number>>;
}

const DrawerContext = React.createContext<DrawerContextType>({
containerWidth: 0,
setContainerWidth: () => {},
});

interface DrawerContextProviderProps {
children: React.ReactNode;
className?: string;
}

export const DrawerContextProvider = ({children, className}: DrawerContextProviderProps) => {
const [containerWidth, setContainerWidth] = React.useState(0);
const containerRef = React.useRef<HTMLDivElement>(null);

React.useEffect(() => {
if (!containerRef.current) {
return undefined;
}

const updateWidth = () => {
if (containerRef.current) {
setContainerWidth(containerRef.current.clientWidth);
}
};

// Set initial width
updateWidth();

// Update width on resize
const resizeObserver = new ResizeObserver(updateWidth);
resizeObserver.observe(containerRef.current);

return () => {
if (containerRef.current) {

Check warning on line 47 in src/components/Drawer/DrawerContext.tsx

View workflow job for this annotation

GitHub Actions / Verify Files

The ref value 'containerRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'containerRef.current' to a variable inside the effect, and use that variable in the cleanup function
resizeObserver.disconnect();
}
};
}, []);

// Memoize the context value to prevent unnecessary re-renders
const value = React.useMemo(
() => ({
containerWidth,
setContainerWidth,
}),
[containerWidth],
);

return (
<DrawerContext.Provider value={value}>
<div ref={containerRef} className={b('drawer-container', className)}>
{children}
</div>
</DrawerContext.Provider>
);
};

export const useDrawerContext = (): DrawerContextType => {
const context = React.useContext(DrawerContext);

if (context === undefined) {
throw Error('useDrawerContext must be used within a DrawerContextProvider');
}

return context;
};
3 changes: 3 additions & 0 deletions src/components/Drawer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export {DrawerWrapper} from './Drawer';
export {useDrawerContext} from './DrawerContext';
export type {DrawerContextType} from './DrawerContext';
3 changes: 3 additions & 0 deletions src/components/Search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface SearchProps {
className?: string;
debounce?: number;
placeholder?: string;
inputRef?: React.RefObject<HTMLInputElement>;
}

export const Search = ({
Expand All @@ -24,6 +25,7 @@ export const Search = ({
className,
debounce = 200,
placeholder,
inputRef,
}: SearchProps) => {
const [searchValue, setSearchValue] = React.useState<string>(value);

Expand Down Expand Up @@ -52,6 +54,7 @@ export const Search = ({
<TextInput
hasClear
autoFocus
controlRef={inputRef}
style={{width}}
className={b(null, className)}
placeholder={placeholder}
Expand Down
16 changes: 15 additions & 1 deletion src/components/SyntaxHighlighter/YDBSyntaxHighlighter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ export function YDBSyntaxHighlighter({
return null;
};

let paddingStyles = {};

if (
withClipboardButton &&
typeof withClipboardButton === 'object' &&
withClipboardButton.alwaysVisible
) {
if (withClipboardButton.withLabel) {
paddingStyles = {paddingRight: 80};
} else {
paddingStyles = {paddingRight: 40};
}
}

return (
<div className={b(null, className)}>
{renderCopyButton()}
Expand All @@ -96,7 +110,7 @@ export function YDBSyntaxHighlighter({
key={highlighterKey}
language={language}
style={style}
customStyle={{height: '100%'}}
customStyle={{height: '100%', ...paddingStyles}}
>
{text}
</ReactSyntaxHighlighter>
Expand Down
10 changes: 8 additions & 2 deletions src/containers/Tenant/Diagnostics/Diagnostics.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
@use '../../../styles/mixins.scss';

.kv-tenant-diagnostics {
--diagnostics-margin-top: var(--g-spacing-4);
display: flex;
overflow: hidden;
flex-direction: column;

height: 100%;

&__header-wrapper {
padding: 0 20px 16px;
padding: 0 var(--g-spacing-5);

background-color: var(--g-color-base-background);
}
Expand Down Expand Up @@ -39,7 +40,12 @@
flex-grow: 1;

width: 100%;
padding: 0 20px;

// Margin is not counted in height
// thats why we need to subtract it.
height: calc(100% - var(--diagnostics-margin-top));
margin-top: var(--diagnostics-margin-top);
padding: 0 var(--g-spacing-5);

.ydb-table-with-controls-layout {
&__controls {
Expand Down
9 changes: 6 additions & 3 deletions src/containers/Tenant/Diagnostics/Diagnostics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Link} from 'react-router-dom';
import {StringParam, useQueryParams} from 'use-query-params';

import {AutoRefreshControl} from '../../../components/AutoRefreshControl/AutoRefreshControl';
import {DrawerContextProvider} from '../../../components/Drawer/DrawerContext';
import {useFeatureFlagsAvailable} from '../../../store/reducers/capabilities/hooks';
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants';
import {setDiagnosticsTab} from '../../../store/reducers/tenant/tenant';
Expand Down Expand Up @@ -194,9 +195,11 @@ function Diagnostics(props: DiagnosticsProps) {
</Helmet>
) : null}
{renderTabs()}
<div className={b('page-wrapper')} ref={containerRef}>
{renderTabContent()}
</div>
<DrawerContextProvider>
<div className={b('page-wrapper')} ref={containerRef}>
{renderTabContent()}
</div>
</DrawerContextProvider>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,13 @@ export function getTenantOverviewTopQueriesColumns() {
return [queryHashColumn, oneLineQueryTextColumn, cpuTimeUsColumn];
}
export function getRunningQueriesColumns() {
const columns = [userSIDColumn, queryStartColumn, queryTextColumn, applicationColumn];
const columns = [
queryHashColumn,
userSIDColumn,
queryStartColumn,
queryTextColumn,
applicationColumn,
];

return columns.map((column) => ({
...column,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const DEFAULT_TOP_QUERIES_COLUMNS: QueriesColumnId[] = [
export const REQUIRED_TOP_QUERIES_COLUMNS: QueriesColumnId[] = ['CPUTime', 'QueryText'];

export const DEFAULT_RUNNING_QUERIES_COLUMNS: QueriesColumnId[] = [
'QueryHash',
'UserSID',
'QueryStartAt',
'QueryText',
Expand Down
Loading
Loading