Skip to content

Commit 32df3a9

Browse files
committed
feat: redesign Memory section
1 parent 867a6cc commit 32df3a9

File tree

12 files changed

+685
-41
lines changed

12 files changed

+685
-41
lines changed

src/components/MemoryViewer/MemoryViewer.scss

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
$memory-type-colors: (
2-
'AllocatorCachesMemory': var(--g-color-base-utility-medium-hover),
3-
'SharedCacheConsumption': var(--g-color-base-info-medium-hover),
4-
'MemTableConsumption': var(--g-color-base-warning-medium-hover),
5-
'QueryExecutionConsumption': var(--g-color-base-positive-medium-hover),
6-
'Other': var(--g-color-base-generic-medium-hover),
2+
'SharedCacheConsumption': var(--g-color-base-info-medium),
3+
'QueryExecutionConsumption': var(--g-color-base-positive-medium),
4+
'MemTableConsumption': var(--g-color-base-warning-medium),
5+
'AllocatorCachesMemory': var(--g-color-base-danger-medium),
6+
'Other': var(--g-color-base-neutral-medium),
77
);
88

99
@mixin memory-type-color($type) {

src/components/MemoryViewer/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
"text_usage": "Usage",
88
"text_soft-limit": "Soft Limit",
99
"text_hard-limit": "Hard Limit",
10-
"text_other": "Other"
10+
"text_other": "Other",
11+
"text_memory-details": "Memory Details"
1112
}

src/components/MemoryViewer/utils.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,27 @@ export function getMaybeNumber(value: string | number | undefined): number | und
1313
return isNumeric(value) ? parseFloat(String(value)) : undefined;
1414
}
1515

16-
interface MemorySegment {
16+
export interface MemorySegment {
1717
label: string;
1818
key: string;
1919
value: number;
2020
capacity?: number;
2121
isInfo?: boolean;
2222
}
2323

24+
// Memory segment colors using CSS variables for theme support
25+
export const MEMORY_SEGMENT_COLORS: Record<string, string> = {
26+
SharedCacheConsumption: 'var(--g-color-base-info-medium)',
27+
QueryExecutionConsumption: 'var(--g-color-base-positive-medium)',
28+
MemTableConsumption: 'var(--g-color-base-warning-medium)',
29+
AllocatorCachesMemory: 'var(--g-color-base-danger-medium)',
30+
Other: 'var(--g-color-base-neutral-medium)',
31+
};
32+
33+
export function getMemorySegmentColor(key: string): string {
34+
return MEMORY_SEGMENT_COLORS[key] || MEMORY_SEGMENT_COLORS['Other'];
35+
}
36+
2437
export function getMemorySegments(stats: TMemoryStats, memoryUsage: number): MemorySegment[] {
2538
const segments = [
2639
{
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
@use '../../../../../styles/mixins.scss';
2+
3+
.memory-details {
4+
display: flex;
5+
flex-direction: column;
6+
align-items: flex-start;
7+
align-self: stretch;
8+
gap: var(--g-spacing-3);
9+
10+
&__header {
11+
align-self: stretch;
12+
}
13+
14+
&__title {
15+
font-weight: 500;
16+
@include mixins.subheader-1-typography();
17+
}
18+
19+
&__content {
20+
display: flex;
21+
flex-direction: column;
22+
align-items: flex-start;
23+
align-self: stretch;
24+
gap: var(--g-spacing-4);
25+
}
26+
27+
&__main-progress {
28+
align-self: stretch;
29+
}
30+
31+
&__main-progress-bar {
32+
height: 20px;
33+
}
34+
35+
&__segments-container {
36+
display: flex;
37+
flex-direction: column;
38+
align-items: flex-start;
39+
align-self: stretch;
40+
gap: var(--g-spacing-2);
41+
42+
margin-bottom: var(--g-spacing-4);
43+
}
44+
45+
&__segment-row {
46+
display: flex;
47+
align-items: center;
48+
align-self: stretch;
49+
gap: var(--g-spacing-1);
50+
}
51+
52+
&__segment-indicator {
53+
flex-shrink: 0;
54+
55+
width: 8px;
56+
height: 8px;
57+
58+
border-radius: 50%;
59+
}
60+
61+
&__segment-definition-list {
62+
flex: 1;
63+
}
64+
65+
&__segment-progress {
66+
flex-shrink: 0;
67+
68+
width: 400px;
69+
}
70+
71+
&__progress-bar {
72+
height: 10px;
73+
}
74+
75+
&__popup-container {
76+
display: flex;
77+
align-items: center;
78+
gap: var(--g-spacing-2);
79+
}
80+
81+
&__popup-legend {
82+
flex-shrink: 0;
83+
84+
width: 12px;
85+
height: 12px;
86+
87+
border-radius: var(--g-border-radius-xs);
88+
}
89+
90+
&__popup-name {
91+
color: var(--g-color-text-primary);
92+
}
93+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import {DefinitionList, Flex, Progress, Text} from '@gravity-ui/uikit';
2+
3+
import {HoverPopup} from '../../../../../components/HoverPopup/HoverPopup';
4+
import i18n from '../../../../../components/MemoryViewer/i18n';
5+
import {
6+
getMemorySegmentColor,
7+
getMemorySegments,
8+
} from '../../../../../components/MemoryViewer/utils';
9+
import {ProgressViewer} from '../../../../../components/ProgressViewer/ProgressViewer';
10+
import type {TMemoryStats} from '../../../../../types/api/nodes';
11+
import {formatBytes} from '../../../../../utils/bytesParsers';
12+
import {cn} from '../../../../../utils/cn';
13+
import {ProgressWrapper} from '../TenantStorage/ProgressWrapper';
14+
15+
import './MemoryDetailsSection.scss';
16+
17+
const b = cn('memory-details');
18+
19+
interface MemoryDetailsSectionProps {
20+
memoryStats: TMemoryStats;
21+
}
22+
23+
export function MemoryDetailsSection({memoryStats}: MemoryDetailsSectionProps) {
24+
let memoryUsage: number;
25+
if (memoryStats.AnonRss === undefined) {
26+
memoryUsage =
27+
Number(memoryStats.AllocatedMemory || 0) +
28+
Number(memoryStats.AllocatorCachesMemory || 0);
29+
} else {
30+
memoryUsage = Number(memoryStats.AnonRss);
31+
}
32+
33+
const memorySegments = getMemorySegments(memoryStats, Number(memoryUsage));
34+
const displaySegments = memorySegments.filter(
35+
(segment) => !segment.isInfo && segment.value > 0,
36+
);
37+
38+
return (
39+
<div className={b()}>
40+
<div className={b('header')}>
41+
<Text variant="body-1" className={b('title')}>
42+
{i18n('text_memory-details')}
43+
</Text>
44+
</div>
45+
<div className={b('content')}>
46+
<div className={b('main-progress')}>
47+
<HoverPopup
48+
renderPopupContent={() => (
49+
<DefinitionList responsive>
50+
{memorySegments.map(
51+
({
52+
label,
53+
value: segmentSize,
54+
capacity: segmentCapacity,
55+
key,
56+
}) => (
57+
<DefinitionList.Item
58+
key={label}
59+
name={
60+
<div className={b('popup-container')}>
61+
<div
62+
className={b('popup-legend')}
63+
style={{
64+
backgroundColor:
65+
getMemorySegmentColor(key),
66+
}}
67+
/>
68+
<div className={b('popup-name')}>{label}</div>
69+
</div>
70+
}
71+
>
72+
{segmentCapacity ? (
73+
<ProgressViewer
74+
value={segmentSize}
75+
capacity={segmentCapacity}
76+
formatValues={(
77+
value?: number,
78+
total?: number,
79+
): [string, string] => [
80+
formatBytes({
81+
value: value || 0,
82+
size: 'gb',
83+
withSizeLabel: false,
84+
precision: 2,
85+
}),
86+
formatBytes({
87+
value: total || 0,
88+
size: 'gb',
89+
withSizeLabel: true,
90+
precision: 1,
91+
}),
92+
]}
93+
colorizeProgress
94+
/>
95+
) : (
96+
formatBytes({
97+
value: segmentSize,
98+
size: 'gb',
99+
withSizeLabel: true,
100+
precision: 2,
101+
})
102+
)}
103+
</DefinitionList.Item>
104+
),
105+
)}
106+
</DefinitionList>
107+
)}
108+
>
109+
<ProgressWrapper
110+
stack={memorySegments}
111+
totalCapacity={memoryStats.HardLimit}
112+
formatValues={(value?: number, total?: number): [string, string] => [
113+
formatBytes({
114+
value: value || 0,
115+
size: 'gb',
116+
withSizeLabel: false,
117+
precision: 2,
118+
}),
119+
formatBytes({
120+
value: total || 0,
121+
size: 'gb',
122+
withSizeLabel: true,
123+
precision: 1,
124+
}),
125+
]}
126+
className={b('main-progress-bar')}
127+
size="m"
128+
width="full"
129+
/>
130+
</HoverPopup>
131+
</div>
132+
<div className={b('segments-container')}>
133+
{displaySegments.map((segment) => {
134+
const segmentColor = getMemorySegmentColor(segment.key);
135+
let valueText: string;
136+
if (segment.capacity) {
137+
valueText = `${formatBytes({value: segment.value, size: 'tb', withSizeLabel: false, precision: 2})} of ${formatBytes({value: segment.capacity, size: 'tb', withSizeLabel: true, precision: 0})}`;
138+
} else {
139+
valueText = formatBytes({
140+
value: segment.value,
141+
size: 'gb',
142+
withSizeLabel: true,
143+
precision: 1,
144+
});
145+
}
146+
147+
return (
148+
<div key={segment.key} className={b('segment-row')}>
149+
<div
150+
className={b('segment-indicator')}
151+
style={{backgroundColor: segmentColor}}
152+
/>
153+
<DefinitionList
154+
nameMaxWidth={200}
155+
className={b('segment-definition-list')}
156+
>
157+
<DefinitionList.Item
158+
name={
159+
<Text variant="body-1" color="secondary">
160+
{segment.label}
161+
</Text>
162+
}
163+
>
164+
<Flex alignItems="center" gap="3">
165+
<div className={b('segment-progress')}>
166+
<div
167+
style={
168+
{
169+
'--g-progress-filled-background-color':
170+
getMemorySegmentColor(segment.key),
171+
} as React.CSSProperties
172+
}
173+
>
174+
<Progress
175+
value={
176+
segment.capacity
177+
? (segment.value /
178+
segment.capacity) *
179+
100
180+
: 100
181+
}
182+
size="s"
183+
className={b('progress-bar')}
184+
/>
185+
</div>
186+
</div>
187+
<Text variant="body-1" color="secondary">
188+
{valueText}
189+
</Text>
190+
</Flex>
191+
</DefinitionList.Item>
192+
</DefinitionList>
193+
</div>
194+
);
195+
})}
196+
</div>
197+
</div>
198+
</div>
199+
);
200+
}

0 commit comments

Comments
 (0)