Skip to content

feat: redesign Memory section #2618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
20 changes: 20 additions & 0 deletions src/components/InfoViewer/InfoViewer.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use '../../styles/mixins.scss';

.info-viewer {
--ydb-info-viewer-font-size: var(--g-text-body-2-font-size);
--ydb-info-viewer-line-height: var(--g-text-body-2-line-height);
Expand Down Expand Up @@ -28,6 +30,10 @@

max-width: 100%;
padding-top: 4px;

&:first-child {
padding-top: 0;
}
}

&__label {
Expand Down Expand Up @@ -88,4 +94,18 @@
}
}
}

&_variant_small {
.info-viewer__title {
margin: 0 0 var(--g-spacing-3);

color: var(--g-color-text-primary);
@include mixins.subheader-1-typography();
}

.info-viewer__label {
color: var(--g-color-text-secondary);
@include mixins.body-1-typography();
}
}
}
4 changes: 3 additions & 1 deletion src/components/InfoViewer/InfoViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface InfoViewerProps {
info?: InfoViewerItem[];
dots?: boolean;
size?: 's';
variant?: 'default' | 'small';
className?: string;
multilineLabels?: boolean;
renderEmptyState?: (props?: Pick<InfoViewerProps, 'title' | 'size'>) => React.ReactNode;
Expand All @@ -28,6 +29,7 @@ export const InfoViewer = ({
info,
dots = true,
size,
variant = 'default',
className,
multilineLabels,
renderEmptyState,
Expand All @@ -37,7 +39,7 @@ export const InfoViewer = ({
}

return (
<div className={b({size}, className)}>
<div className={b({size, variant}, className)}>
{title && <div className={b('title')}>{title}</div>}
{info && info.length > 0 ? (
<div className={b('items')}>
Expand Down
10 changes: 5 additions & 5 deletions src/components/MemoryViewer/MemoryViewer.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
$memory-type-colors: (
'AllocatorCachesMemory': var(--g-color-base-utility-medium-hover),
'SharedCacheConsumption': var(--g-color-base-info-medium-hover),
'MemTableConsumption': var(--g-color-base-warning-medium-hover),
'QueryExecutionConsumption': var(--g-color-base-positive-medium-hover),
'Other': var(--g-color-base-generic-medium-hover),
'SharedCacheConsumption': var(--g-color-base-info-medium),
'QueryExecutionConsumption': var(--g-color-base-positive-medium),
'MemTableConsumption': var(--g-color-base-warning-medium),
'AllocatorCachesMemory': var(--g-color-base-danger-medium),
'Other': var(--g-color-base-neutral-medium),
);

@mixin memory-type-color($type) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/MemoryViewer/MemoryViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type {TMemoryStats} from '../../types/api/nodes';
import {formatBytes} from '../../utils/bytesParsers';
import {cn} from '../../utils/cn';
import {GIGABYTE} from '../../utils/constants';
import type {FormatProgressViewerValues} from '../../utils/progress';
import {calculateProgressStatus} from '../../utils/progress';
import {isNumeric} from '../../utils/utils';
import {HoverPopup} from '../HoverPopup/HoverPopup';
import type {FormatProgressViewerValues} from '../ProgressViewer/ProgressViewer';
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';

import {calculateAllocatedMemory, getMemorySegments} from './utils';
Expand Down
3 changes: 2 additions & 1 deletion src/components/MemoryViewer/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"text_usage": "Usage",
"text_soft-limit": "Soft Limit",
"text_hard-limit": "Hard Limit",
"text_other": "Other"
"text_other": "Other",
"text_memory-details": "Memory Details"
}
15 changes: 14 additions & 1 deletion src/components/MemoryViewer/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,27 @@ export function getMaybeNumber(value: string | number | undefined): number | und
return isNumeric(value) ? parseFloat(String(value)) : undefined;
}

interface MemorySegment {
export interface MemorySegment {
label: string;
key: string;
value: number;
capacity?: number;
isInfo?: boolean;
}

// Memory segment colors using CSS variables for theme support
export const MEMORY_SEGMENT_COLORS: Record<string, string> = {
SharedCacheConsumption: 'var(--g-color-base-info-medium)',
QueryExecutionConsumption: 'var(--g-color-base-positive-medium)',
MemTableConsumption: 'var(--g-color-base-warning-medium)',
AllocatorCachesMemory: 'var(--g-color-base-danger-medium)',
Other: 'var(--g-color-base-neutral-medium)',
};

export function getMemorySegmentColor(key: string): string {
return MEMORY_SEGMENT_COLORS[key] || MEMORY_SEGMENT_COLORS['Other'];
}

export function getMemorySegments(stats: TMemoryStats, memoryUsage: number): MemorySegment[] {
const segments = [
{
Expand Down
19 changes: 3 additions & 16 deletions src/components/ProgressViewer/ProgressViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useTheme} from '@gravity-ui/uikit';

import {cn} from '../../utils/cn';
import {formatNumber, roundToPrecision} from '../../utils/dataFormatters/dataFormatters';
import {calculateProgressStatus} from '../../utils/progress';
import {calculateProgressStatus, defaultFormatProgressValues} from '../../utils/progress';
import type {FormatProgressViewerValues} from '../../utils/progress';
import {isNumeric} from '../../utils/utils';

import './ProgressViewer.scss';
Expand All @@ -11,19 +11,6 @@ const b = cn('progress-viewer');

type ProgressViewerSize = 'xs' | 's' | 'ns' | 'm' | 'n' | 'l' | 'head';

export type FormatProgressViewerValues = (
value?: number,
capacity?: number,
) => (string | number | undefined)[];

const formatValue = (value?: number) => {
return formatNumber(roundToPrecision(Number(value), 2));
};

const defaultFormatValues: FormatProgressViewerValues = (value, total) => {
return [formatValue(value), formatValue(total)];
};

/*

Props description:
Expand Down Expand Up @@ -56,7 +43,7 @@ export interface ProgressViewerProps {
export function ProgressViewer({
value,
capacity,
formatValues = defaultFormatValues,
formatValues = defaultFormatProgressValues,
percents,
withOverflow,
className,
Expand Down
4 changes: 4 additions & 0 deletions src/components/ProgressViewer/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"value_of_capacity": "{{value}} of {{capacity}}",
"no-data": "No data"
}
7 changes: 7 additions & 0 deletions src/components/ProgressViewer/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {registerKeysets} from '../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'ydb-progress-viewer';

export default registerKeysets(COMPONENT, {en});
25 changes: 25 additions & 0 deletions src/components/ProgressWrapper/ProgressContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {Flex, Text} from '@gravity-ui/uikit';

import {getProgressStyle} from './progressUtils';
import type {ProgressContainerProps} from './types';

export function ProgressContainer({
children,
displayText,
withValue = false,
className,
width,
}: ProgressContainerProps) {
const progressStyle = getProgressStyle(width);

return (
<Flex alignItems="center" gap="2" className={className}>
<div style={progressStyle}>{children}</div>
{withValue && displayText && (
<Text variant="body-1" color="secondary">
{displayText}
</Text>
)}
</Flex>
);
}
10 changes: 10 additions & 0 deletions src/components/ProgressWrapper/ProgressWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {SingleProgress} from './SingleProgress';
import {StackProgress} from './StackProgress';
import type {ProgressWrapperProps} from './types';

export function ProgressWrapper(props: ProgressWrapperProps) {
if ('stack' in props && props.stack) {
return <StackProgress {...props} />;
}
return <SingleProgress {...props} />;
}
54 changes: 54 additions & 0 deletions src/components/ProgressWrapper/SingleProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';

import {Progress} from '@gravity-ui/uikit';

import {defaultFormatProgressValues} from '../../utils/progress';
import {safeParseNumber} from '../../utils/utils';

import {ProgressContainer} from './ProgressContainer';
import i18n from './i18n';
import {
PROGRESS_SIZE,
calculateProgressWidth,
formatDisplayValues,
formatProgressText,
isValidValue,
} from './progressUtils';
import type {ProgressWrapperSingleProps} from './types';

export function SingleProgress({
value,
capacity,
formatValues = defaultFormatProgressValues,
className,
width,
size = PROGRESS_SIZE,
withValue = false,
}: ProgressWrapperSingleProps) {
if (!isValidValue(value)) {
return <div className={className}>{i18n('alert_no-data')}</div>;
}

const numericValue = safeParseNumber(value);
const numericCapacity = safeParseNumber(capacity);
const clampedFillWidth = calculateProgressWidth(numericValue, numericCapacity);

const [valueText, capacityText] = React.useMemo(() => {
return formatDisplayValues(value, capacity, formatValues);
}, [formatValues, value, capacity]);

const displayText = React.useMemo(() => {
return formatProgressText(valueText, capacityText, numericCapacity);
}, [valueText, capacityText, numericCapacity]);

return (
<ProgressContainer
displayText={displayText}
withValue={withValue}
className={className}
width={width}
>
<Progress value={clampedFillWidth} theme="success" size={size} />
</ProgressContainer>
);
}
72 changes: 72 additions & 0 deletions src/components/ProgressWrapper/StackProgress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';

import {Progress} from '@gravity-ui/uikit';

import {defaultFormatProgressValues} from '../../utils/progress';
import {safeParseNumber} from '../../utils/utils';
import {getMemorySegmentColor} from '../MemoryViewer/utils';

import {ProgressContainer} from './ProgressContainer';
import i18n from './i18n';
import {
MAX_PERCENTAGE,
PROGRESS_SIZE,
formatDisplayValues,
formatProgressText,
} from './progressUtils';
import type {ProgressWrapperStackProps} from './types';

export function StackProgress({
stack,
totalCapacity,
formatValues = defaultFormatProgressValues,
className,
width,
size = PROGRESS_SIZE,
withValue = false,
}: ProgressWrapperStackProps) {
const displaySegments = React.useMemo(() => {
return stack.filter((segment) => !segment.isInfo && segment.value > 0);
}, [stack]);

if (displaySegments.length === 0) {
return <div className={className}>{i18n('alert_no-data')}</div>;
}

const totalValue = React.useMemo(() => {
return displaySegments.reduce((sum, segment) => sum + segment.value, 0);
}, [displaySegments]);

const numericTotalCapacity = React.useMemo(() => {
return safeParseNumber(totalCapacity);
}, [totalCapacity]);

const maxValue = numericTotalCapacity || totalValue;

const stackElements = React.useMemo(() => {
return displaySegments.map((segment) => ({
value: maxValue > 0 ? (segment.value / maxValue) * MAX_PERCENTAGE : 0,
color: getMemorySegmentColor(segment.key),
title: segment.label,
}));
}, [displaySegments, maxValue]);

const [totalValueText, totalCapacityText] = React.useMemo(() => {
return formatDisplayValues(totalValue, numericTotalCapacity || totalValue, formatValues);
}, [formatValues, totalValue, numericTotalCapacity]);

const displayText = React.useMemo(() => {
return formatProgressText(totalValueText, totalCapacityText, numericTotalCapacity || 0);
}, [totalValueText, totalCapacityText, numericTotalCapacity]);

return (
<ProgressContainer
displayText={displayText}
withValue={withValue}
className={className}
width={width}
>
<Progress value={MAX_PERCENTAGE} stack={stackElements} size={size} />
</ProgressContainer>
);
}
4 changes: 4 additions & 0 deletions src/components/ProgressWrapper/i18n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"alert_no-data": "no data",
"context_capacity-usage": "{{value}} of {{capacity}}"
}
11 changes: 11 additions & 0 deletions src/components/ProgressWrapper/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {registerKeysets} from '../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'progress-wrapper';

const keysets = {
en,
};

export default registerKeysets(COMPONENT, keysets);
18 changes: 18 additions & 0 deletions src/components/ProgressWrapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Main component - public API
export {ProgressWrapper} from './ProgressWrapper';

// Individual components - for direct usage if needed
export {SingleProgress} from './SingleProgress';
export {StackProgress} from './StackProgress';
export {ProgressContainer} from './ProgressContainer';

// Types - for consumers
export type {
ProgressWrapperProps,
ProgressWrapperSingleProps,
ProgressWrapperStackProps,
ProgressContainerProps,
} from './types';

// Utils - for advanced usage
export * from './progressUtils';
Loading
Loading