diff --git a/package-lock.json b/package-lock.json
index d17bab5232..4f471a6d18 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
"@gravity-ui/icons": "^2.9.1",
"@gravity-ui/navigation": "^2.7.0",
"@gravity-ui/paranoid": "^1.4.1",
- "@gravity-ui/react-data-table": "^2.0.1",
+ "@gravity-ui/react-data-table": "^2.1.1",
"@gravity-ui/uikit": "^6.10.2",
"@gravity-ui/websql-autocomplete": "^8.1.0",
"@reduxjs/toolkit": "^2.2.3",
@@ -3901,9 +3901,9 @@
}
},
"node_modules/@gravity-ui/react-data-table": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@gravity-ui/react-data-table/-/react-data-table-2.0.1.tgz",
- "integrity": "sha512-b926wU0jHEJyLS28VnYLD88IICYqUI6ys1YEnM/+vIUcyJ/9fUP56xQFCrlLZnxbqww6TIXEQ9RC3GJ5qJwdaA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@gravity-ui/react-data-table/-/react-data-table-2.1.1.tgz",
+ "integrity": "sha512-7h5Idn1hRrdjVr46xDfJ57I5Zu9n4biV8ytuYCKBoj2LpuH4t93VHLGy+E1CTx87gjoDLPCVI6FEGAI/iyQJ3Q==",
"dependencies": {
"@bem-react/classname": ">=1.6.0",
"react-list": "^0.8.17",
diff --git a/package.json b/package.json
index d93b8444b5..f8e29aedcd 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
"@gravity-ui/icons": "^2.9.1",
"@gravity-ui/navigation": "^2.7.0",
"@gravity-ui/paranoid": "^1.4.1",
- "@gravity-ui/react-data-table": "^2.0.1",
+ "@gravity-ui/react-data-table": "^2.1.1",
"@gravity-ui/uikit": "^6.10.2",
"@gravity-ui/websql-autocomplete": "^8.1.0",
"@reduxjs/toolkit": "^2.2.3",
diff --git a/src/components/DateRange/DateRange.scss b/src/components/DateRange/DateRange.scss
index 8c036523e2..0efe8594e1 100644
--- a/src/components/DateRange/DateRange.scss
+++ b/src/components/DateRange/DateRange.scss
@@ -1,11 +1,18 @@
.date-range {
&__input {
min-width: 190px;
+ height: 28px;
padding: 5px 8px;
color: var(--g-color-text-primary);
border: 1px solid var(--g-color-line-generic);
border-radius: var(--g-border-radius-m);
+ outline: none;
background: transparent;
}
+
+ &__input:focus,
+ &__input:focus-visible {
+ border: 1px solid var(--g-color-line-generic-hover);
+ }
}
diff --git a/src/components/NodeHostWrapper/NodeHostWrapper.scss b/src/components/NodeHostWrapper/NodeHostWrapper.scss
index 82455f3a55..7b01ea1b18 100644
--- a/src/components/NodeHostWrapper/NodeHostWrapper.scss
+++ b/src/components/NodeHostWrapper/NodeHostWrapper.scss
@@ -1,10 +1,4 @@
.ydb-node-host-wrapper {
- &__host-wrapper {
- display: flex;
-
- width: 330px;
- }
-
&__host {
overflow: hidden;
}
diff --git a/src/components/NodeHostWrapper/NodeHostWrapper.tsx b/src/components/NodeHostWrapper/NodeHostWrapper.tsx
index d731c2720d..6ee1442da0 100644
--- a/src/components/NodeHostWrapper/NodeHostWrapper.tsx
+++ b/src/components/NodeHostWrapper/NodeHostWrapper.tsx
@@ -39,25 +39,18 @@ export const NodeHostWrapper = ({node, getNodeRef}: NodeHostWrapperProps) => {
placement={['top', 'bottom']}
behavior={PopoverBehavior.Immediate}
>
-
-
- {nodeRef && (
-
- )}
-
+
+ {nodeRef && (
+
+ )}
);
};
diff --git a/src/components/QueryResultTable/QueryResultTable.scss b/src/components/QueryResultTable/QueryResultTable.scss
index 8412de2958..41f9d85369 100644
--- a/src/components/QueryResultTable/QueryResultTable.scss
+++ b/src/components/QueryResultTable/QueryResultTable.scss
@@ -2,6 +2,7 @@
.ydb-query-result-table {
&__cell {
+ width: 100%;
@include cell-container;
}
diff --git a/src/components/QueryResultTable/QueryResultTable.tsx b/src/components/QueryResultTable/QueryResultTable.tsx
index 133ec737b2..e92acc98d4 100644
--- a/src/components/QueryResultTable/QueryResultTable.tsx
+++ b/src/components/QueryResultTable/QueryResultTable.tsx
@@ -1,13 +1,15 @@
import React from 'react';
import DataTable from '@gravity-ui/react-data-table';
-import type {Column, DataTableProps, Settings} from '@gravity-ui/react-data-table';
+import type {Column, Settings} from '@gravity-ui/react-data-table';
import type {ColumnType, KeyValueRow} from '../../types/api/query';
import {cn} from '../../utils/cn';
import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
import {getColumnType, prepareQueryResponse} from '../../utils/query';
import {isNumeric} from '../../utils/utils';
+import type {ResizeableDataTableProps} from '../ResizeableDataTable/ResizeableDataTable';
+import {ResizeableDataTable} from '../ResizeableDataTable/ResizeableDataTable';
import {Cell} from './Cell';
import i18n from './i18n';
@@ -70,7 +72,7 @@ const prepareGenericColumns = (data: KeyValueRow[]) => {
const getRowIndex = (_: unknown, index: number) => index;
interface QueryResultTableProps
- extends Omit, 'data' | 'columns' | 'theme'> {
+ extends Omit, 'data' | 'columns'> {
data?: KeyValueRow[];
columns?: ColumnType[];
}
@@ -101,8 +103,7 @@ export const QueryResultTable = (props: QueryResultTableProps) => {
}
return (
- extends Omit, 'theme' | 'onResize'> {
+ columnsWidthLSKey?: string;
+ wrapperClassName?: string;
+}
+
+export function ResizeableDataTable({
+ columnsWidthLSKey,
+ columns,
+ settings,
+ wrapperClassName,
+ ...props
+}: ResizeableDataTableProps) {
+ const [tableColumnsWidth, setTableColumnsWidth] = useTableResize(columnsWidthLSKey);
+
+ const updatedColumns = updateColumnsWidth(columns, tableColumnsWidth);
+
+ const newSettings: Settings = {
+ ...settings,
+ defaultResizeable: true,
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/VirtualTable/ResizeHandler.tsx b/src/components/VirtualTable/ResizeHandler.tsx
new file mode 100644
index 0000000000..3682bd3153
--- /dev/null
+++ b/src/components/VirtualTable/ResizeHandler.tsx
@@ -0,0 +1,106 @@
+import React from 'react';
+
+import {b} from './shared';
+import {calculateColumnWidth, rafThrottle} from './utils';
+
+interface ResizeHandlerProps {
+ maxWidth?: number;
+ minWidth?: number;
+ getCurrentColumnWidth: () => number | undefined;
+ onResize?: (width: number) => void;
+}
+
+export function ResizeHandler({
+ minWidth,
+ maxWidth,
+ getCurrentColumnWidth,
+ onResize,
+}: ResizeHandlerProps) {
+ const elementRef = React.useRef(null);
+
+ const [resizing, setResizing] = React.useState(false);
+
+ React.useEffect(() => {
+ const element = elementRef.current;
+
+ if (!element) {
+ return undefined;
+ }
+
+ let mouseXPosition: number | undefined;
+ let initialColumnWidth: number | undefined;
+ let currentColumnWidth: number | undefined;
+
+ const onMouseMove = rafThrottle((e: MouseEvent) => {
+ restrictMouseEvent(e);
+
+ if (typeof mouseXPosition !== 'number' || typeof initialColumnWidth !== 'number') {
+ return;
+ }
+
+ const xDiff = e.clientX - mouseXPosition;
+
+ const newWidth = calculateColumnWidth(initialColumnWidth + xDiff, minWidth, maxWidth);
+
+ if (newWidth === currentColumnWidth) {
+ return;
+ }
+
+ currentColumnWidth = newWidth;
+
+ onResize?.(currentColumnWidth);
+ });
+
+ const onMouseUp = (e: MouseEvent) => {
+ restrictMouseEvent(e);
+
+ if (currentColumnWidth !== undefined) {
+ onResize?.(currentColumnWidth);
+ }
+
+ setResizing(false);
+ mouseXPosition = undefined;
+
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ };
+
+ const onMouseDown = (e: MouseEvent) => {
+ initialColumnWidth = getCurrentColumnWidth();
+
+ restrictMouseEvent(e);
+
+ mouseXPosition = e.clientX;
+
+ setResizing(true);
+
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+ };
+
+ element.addEventListener('mousedown', onMouseDown);
+
+ return () => {
+ element.removeEventListener('mousedown', onMouseDown);
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ };
+ }, [onResize, minWidth, maxWidth, getCurrentColumnWidth]);
+
+ return (
+ restrictMouseEvent(e)}
+ />
+ );
+}
+
+// Prevent sort trigger and text selection on resize
+function restrictMouseEvent<
+ T extends {preventDefault: VoidFunction; stopPropagation: VoidFunction},
+>(e: T) {
+ e.preventDefault();
+ e.stopPropagation();
+}
diff --git a/src/components/VirtualTable/ResizeableVirtualTable.tsx b/src/components/VirtualTable/ResizeableVirtualTable.tsx
new file mode 100644
index 0000000000..b44520962c
--- /dev/null
+++ b/src/components/VirtualTable/ResizeableVirtualTable.tsx
@@ -0,0 +1,31 @@
+import type {ColumnWidthByName} from '@gravity-ui/react-data-table';
+
+import {useTableResize} from '../../utils/hooks/useTableResize';
+
+import type {VirtualTableProps} from './VirtualTable';
+import {VirtualTable} from './VirtualTable';
+import type {Column} from './types';
+
+function updateColumnsWidth(columns: Column[], columnsWidthSetup: ColumnWidthByName) {
+ return columns.map((column) => {
+ return {...column, width: columnsWidthSetup[column.name] ?? column.width};
+ });
+}
+
+interface ResizeableVirtualTableProps extends Omit, 'onColumnsResize'> {
+ columnsWidthLSKey: string;
+}
+
+export function ResizeableVirtualTable({
+ columnsWidthLSKey,
+ columns,
+ ...props
+}: ResizeableVirtualTableProps) {
+ const [tableColumnsWidth, setTableColumnsWidth] = useTableResize(columnsWidthLSKey);
+
+ const updatedColumns = updateColumnsWidth(columns, tableColumnsWidth);
+
+ return (
+
+ );
+}
diff --git a/src/components/VirtualTable/TableHead.tsx b/src/components/VirtualTable/TableHead.tsx
index e477206fc6..0c46db94cd 100644
--- a/src/components/VirtualTable/TableHead.tsx
+++ b/src/components/VirtualTable/TableHead.tsx
@@ -1,10 +1,6 @@
import React from 'react';
-import type {
- HandleTableColumnsResize,
- TableColumnsWidthSetup,
-} from '../../utils/hooks/useTableResize';
-
+import {ResizeHandler} from './ResizeHandler';
import {
ASCENDING,
DEFAULT_RESIZEABLE,
@@ -13,9 +9,7 @@ import {
DESCENDING,
} from './constants';
import {b} from './shared';
-import type {Column, OnSort, SortOrderType, SortParams} from './types';
-
-const COLUMN_NAME_HTML_ATTRIBUTE = 'data-columnname';
+import type {Column, HandleTableColumnsResize, OnSort, SortOrderType, SortParams} from './types';
// Icon similar to original DataTable icons to keep the same tables across diferent pages and tabs
const SortIcon = ({order}: {order?: SortOrderType}) => {
@@ -58,6 +52,7 @@ interface TableHeadCellProps {
rowHeight: number;
onCellMount?: (element: Element) => void;
onCellUnMount?: (element: Element) => void;
+ onColumnsResize?: HandleTableColumnsResize;
}
export const TableHeadCell = ({
@@ -69,6 +64,7 @@ export const TableHeadCell = ({
rowHeight,
onCellMount,
onCellUnMount,
+ onColumnsResize,
}: TableHeadCellProps) => {
const cellWrapperRef = React.useRef(null);
@@ -84,20 +80,28 @@ export const TableHeadCell = ({
};
}, [onCellMount, onCellUnMount]);
+ const getCurrentColumnWidth = React.useCallback(() => {
+ return cellWrapperRef.current?.getBoundingClientRect().width;
+ }, []);
+
+ const handleResize = React.useCallback(
+ (newWidth: number) => {
+ onColumnsResize?.(column.name, newWidth);
+ },
+ [onColumnsResize, column.name],
+ );
+
const content = column.header ?? column.name;
return (
({
defaultSortOrder={defaultSortOrder}
/>
+ {resizeable ? (
+
+ ) : null}
|
);
@@ -140,38 +152,6 @@ export const TableHead = ({
}: TableHeadProps) => {
const [sortParams, setSortParams] = React.useState({});
- const isTableResizeable = Boolean(onColumnsResize);
-
- const resizeObserver: ResizeObserver | undefined = React.useMemo(() => {
- if (!isTableResizeable) {
- return undefined;
- }
-
- return new ResizeObserver((entries) => {
- const columnsWidth: TableColumnsWidthSetup = {};
- entries.forEach((entry) => {
- // @ts-expect-error ignore custrom property usage
- const id = entry.target.attributes[COLUMN_NAME_HTML_ATTRIBUTE]?.value;
- columnsWidth[id] = entry.contentRect.width;
- });
-
- onColumnsResize?.(columnsWidth);
- });
- }, [onColumnsResize, isTableResizeable]);
-
- const handleCellMount = React.useCallback(
- (element: Element) => {
- resizeObserver?.observe(element);
- },
- [resizeObserver],
- );
- const handleCellUnMount = React.useCallback(
- (element: Element) => {
- resizeObserver?.unobserve(element);
- },
- [resizeObserver],
- );
-
const handleSort = (columnId: string) => {
let newSortParams: SortParams = {};
@@ -231,8 +211,7 @@ export const TableHead = ({
defaultSortOrder={defaultSortOrder}
onSort={handleSort}
rowHeight={rowHeight}
- onCellMount={handleCellMount}
- onCellUnMount={handleCellUnMount}
+ onColumnsResize={onColumnsResize}
/>
);
})}
diff --git a/src/components/VirtualTable/VirtualTable.scss b/src/components/VirtualTable/VirtualTable.scss
index 77441fc6c5..008952b8d2 100644
--- a/src/components/VirtualTable/VirtualTable.scss
+++ b/src/components/VirtualTable/VirtualTable.scss
@@ -60,14 +60,12 @@
}
&__head-cell-wrapper {
+ position: relative;
+
display: flex;
overflow-x: hidden;
border-bottom: $cell-border;
-
- &_resizeable {
- resize: horizontal;
- }
}
&__head-cell {
@@ -145,4 +143,26 @@
}
}
}
+
+ &__resize-handler {
+ position: absolute;
+ top: 0;
+ right: 0;
+
+ visibility: hidden;
+
+ width: 6px;
+ height: 100%;
+
+ cursor: col-resize;
+
+ background-color: var(--g-color-base-generic);
+
+ &_resizing {
+ visibility: visible;
+ }
+ }
+ &__head-cell-wrapper:hover > &__resize-handler {
+ visibility: visible;
+ }
}
diff --git a/src/components/VirtualTable/VirtualTable.tsx b/src/components/VirtualTable/VirtualTable.tsx
index 48beca0705..5b2398a006 100644
--- a/src/components/VirtualTable/VirtualTable.tsx
+++ b/src/components/VirtualTable/VirtualTable.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import type {IResponseError} from '../../types/api/error';
import {getArray} from '../../utils';
-import type {HandleTableColumnsResize} from '../../utils/hooks/useTableResize';
import {ResponseError} from '../Errors/ResponseError';
import {TableWithControlsLayout} from '../TableWithControlsLayout/TableWithControlsLayout';
@@ -25,6 +24,7 @@ import type {
Column,
FetchData,
GetRowClassName,
+ HandleTableColumnsResize,
OnEntry,
OnLeave,
OnSort,
@@ -37,7 +37,7 @@ import {useIntersectionObserver} from './useIntersectionObserver';
import './VirtualTable.scss';
-interface VirtualTableProps {
+export interface VirtualTableProps {
limit: number;
fetchData: FetchData;
columns: Column[];
diff --git a/src/components/VirtualTable/types.ts b/src/components/VirtualTable/types.ts
index 5f6d5a82ae..04cd2a3e77 100644
--- a/src/components/VirtualTable/types.ts
+++ b/src/components/VirtualTable/types.ts
@@ -21,6 +21,8 @@ export type SortOrderType = typeof ASCENDING | typeof DESCENDING;
export type SortParams = {columnId?: string; sortOrder?: SortOrderType};
export type OnSort = (params: SortParams) => void;
+export type HandleTableColumnsResize = (columnId: string, width: number) => void;
+
export interface Column {
name: string;
header?: React.ReactNode;
@@ -29,6 +31,8 @@ export interface Column {
resizeable?: boolean;
render: (props: {row: T; index: number}) => React.ReactNode;
width: number;
+ resizeMaxWidth?: number;
+ resizeMinWidth?: number;
align: AlignType;
}
diff --git a/src/components/VirtualTable/utils.ts b/src/components/VirtualTable/utils.ts
new file mode 100644
index 0000000000..e546d8e3ee
--- /dev/null
+++ b/src/components/VirtualTable/utils.ts
@@ -0,0 +1,25 @@
+// invoke passed function at most once per animation frame
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function rafThrottle any>(fn: Fn) {
+ let rafId: number | null = null;
+ let latestArgs: Parameters;
+
+ return function throttled(...args: Parameters) {
+ // call throttled function with latest args
+ latestArgs = args;
+
+ if (typeof rafId === 'number') {
+ return;
+ }
+
+ rafId = requestAnimationFrame(() => {
+ fn(...latestArgs);
+ rafId = null;
+ });
+ };
+}
+
+// 40px minWidth so sort icon won't overlap wrapped column title
+export function calculateColumnWidth(newWidth: number, minWidth = 40, maxWidth = Infinity) {
+ return Math.max(minWidth, Math.min(newWidth, maxWidth));
+}
diff --git a/src/containers/ClusterModeGuard/ClusterModeGuard.tsx b/src/containers/ClusterModeGuard/ClusterModeGuard.tsx
index bbb4876303..3aa600cdb4 100644
--- a/src/containers/ClusterModeGuard/ClusterModeGuard.tsx
+++ b/src/containers/ClusterModeGuard/ClusterModeGuard.tsx
@@ -1,6 +1,6 @@
import React from 'react';
-import {useTypedSelector} from '../../lib';
+import {useTypedSelector} from '../../utils/hooks';
export interface ClusterModeGuardProps {
children: React.ReactNode;
diff --git a/src/containers/Clusters/Clusters.scss b/src/containers/Clusters/Clusters.scss
index 4fdef07530..4d8977e0ec 100644
--- a/src/containers/Clusters/Clusters.scss
+++ b/src/containers/Clusters/Clusters.scss
@@ -151,8 +151,13 @@
&__table-content {
overflow: auto;
+ height: 100%;
+ }
+
+ &__table {
@include mixins.freeze-nth-column(1);
}
+
&__balancer-cell {
display: flex;
flex-direction: row;
diff --git a/src/containers/Clusters/Clusters.tsx b/src/containers/Clusters/Clusters.tsx
index 6adfc8f860..b24d13e176 100644
--- a/src/containers/Clusters/Clusters.tsx
+++ b/src/containers/Clusters/Clusters.tsx
@@ -6,6 +6,7 @@ import {Helmet} from 'react-helmet-async';
import {ResponseError} from '../../components/Errors/ResponseError';
import {Loader} from '../../components/Loader';
+import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../components/Search';
import {changeClustersFilters, clustersApi} from '../../store/reducers/clusters/clusters';
import {
@@ -21,7 +22,7 @@ import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
import {getMinorVersion} from '../../utils/versions';
import {ClustersStatistics} from './ClustersStatistics';
-import {CLUSTERS_COLUMNS} from './columns';
+import {CLUSTERS_COLUMNS, CLUSTERS_COLUMNS_WIDTH_LS_KEY} from './columns';
import {
CLUSTER_STATUSES,
COLUMNS_NAMES,
@@ -174,8 +175,9 @@ export function Clusters() {
{query.isError ? : null}
-
—;
export const CLUSTERS_COLUMNS: Column[] = [
@@ -144,7 +146,7 @@ export const CLUSTERS_COLUMNS: Column[] = [
{
name: COLUMNS_NAMES.NODES,
header: COLUMNS_TITLES[COLUMNS_NAMES.NODES],
- width: 150,
+ resizeMinWidth: 140,
defaultOrder: DataTable.DESCENDING,
sortAccessor: ({cluster = {}}) => {
const {NodesTotal = 0} = cluster;
@@ -164,7 +166,7 @@ export const CLUSTERS_COLUMNS: Column[] = [
{
name: COLUMNS_NAMES.LOAD,
header: COLUMNS_TITLES[COLUMNS_NAMES.LOAD],
- width: 150,
+ resizeMinWidth: 140,
defaultOrder: DataTable.DESCENDING,
sortAccessor: ({cluster}) => {
return cluster?.NumberOfCpus;
@@ -182,7 +184,7 @@ export const CLUSTERS_COLUMNS: Column[] = [
{
name: COLUMNS_NAMES.STORAGE,
header: COLUMNS_TITLES[COLUMNS_NAMES.STORAGE],
- width: 150,
+ resizeMinWidth: 140,
defaultOrder: DataTable.DESCENDING,
sortAccessor: ({cluster}) => {
return Number(cluster?.StorageTotal);
diff --git a/src/containers/Nodes/Nodes.scss b/src/containers/Nodes/Nodes.scss
index 74aacb99fd..de91414dda 100644
--- a/src/containers/Nodes/Nodes.scss
+++ b/src/containers/Nodes/Nodes.scss
@@ -12,11 +12,6 @@
margin-bottom: 15px;
}
- &__table {
- @include table-styles;
- @include table-sticky-styles;
- }
-
&__node_unavailable {
opacity: 0.6;
}
diff --git a/src/containers/Nodes/Nodes.tsx b/src/containers/Nodes/Nodes.tsx
index 47e17894bd..f6953ae474 100644
--- a/src/containers/Nodes/Nodes.tsx
+++ b/src/containers/Nodes/Nodes.tsx
@@ -1,6 +1,5 @@
import React from 'react';
-import DataTable from '@gravity-ui/react-data-table';
import {ASCENDING} from '@gravity-ui/react-data-table/build/esm/lib/constants';
import {skipToken} from '@reduxjs/toolkit/query';
@@ -9,6 +8,7 @@ import {AccessDenied} from '../../components/Errors/403';
import {ResponseError} from '../../components/Errors/ResponseError';
import {Illustration} from '../../components/Illustration';
import {ProblemFilter} from '../../components/ProblemFilter';
+import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../components/Search';
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
import {UptimeFilter} from '../../components/UptimeFIlter';
@@ -37,7 +37,7 @@ import {
isUnavailableNode,
} from '../../utils/nodes';
-import {getNodesColumns} from './getNodesColumns';
+import {NODES_COLUMNS_WIDTH_LS_KEY, getNodesColumns} from './getNodesColumns';
import i18n from './i18n';
import './Nodes.scss';
@@ -149,8 +149,8 @@ export const Nodes = ({path, additionalNodesProps = {}}: NodesProps) => {
}
return (
- {
return (
{renderControls()}
-
+
{renderTable()}
diff --git a/src/containers/Nodes/VirtualNodes.tsx b/src/containers/Nodes/VirtualNodes.tsx
index eec5eece28..7facbde17e 100644
--- a/src/containers/Nodes/VirtualNodes.tsx
+++ b/src/containers/Nodes/VirtualNodes.tsx
@@ -7,19 +7,18 @@ import {Illustration} from '../../components/Illustration';
import {ProblemFilter} from '../../components/ProblemFilter';
import {Search} from '../../components/Search';
import {UptimeFilter} from '../../components/UptimeFIlter';
-import {VirtualTable} from '../../components/VirtualTable';
import type {
FetchData,
GetRowClassName,
RenderControls,
RenderErrorMessage,
} from '../../components/VirtualTable';
+import {ResizeableVirtualTable} from '../../components/VirtualTable/ResizeableVirtualTable';
import type {NodesPreparedEntity} from '../../store/reducers/nodes/types';
import {ProblemFilterValues} from '../../store/reducers/settings/settings';
import type {ProblemFilterValue} from '../../store/reducers/settings/types';
import type {AdditionalNodesProps} from '../../types/additionalProps';
import {cn} from '../../utils/cn';
-import {updateColumnsWidth, useTableResize} from '../../utils/hooks/useTableResize';
import {
NodesUptimeFilterValues,
getProblemParamValue,
@@ -30,7 +29,7 @@ import {
import type {NodesSortValue} from '../../utils/nodes';
import {getNodes} from './getNodes';
-import {getNodesColumns} from './getNodesColumns';
+import {NODES_COLUMNS_WIDTH_LS_KEY, getNodesColumns} from './getNodesColumns';
import i18n from './i18n';
import './Nodes.scss';
@@ -52,8 +51,6 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node
NodesUptimeFilterValues.All,
);
- const [tableColumnsWidthSetup, setTableColumnsWidth] = useTableResize('nodesTableColumnsWidth');
-
const filters = React.useMemo(() => {
return [path, searchValue, problemFilter, uptimeFilter];
}, [path, searchValue, problemFilter, uptimeFilter]);
@@ -122,12 +119,13 @@ export const VirtualNodes = ({path, parentContainer, additionalNodesProps}: Node
getNodeRef: additionalNodesProps?.getNodeRef,
});
- const columns = updateColumnsWidth(rawColumns, tableColumnsWidthSetup).map((column) => {
+ const columns = rawColumns.map((column) => {
return {...column, sortable: isSortableNodesProperty(column.name)};
});
return (
-
);
};
diff --git a/src/containers/Nodes/getNodesColumns.tsx b/src/containers/Nodes/getNodesColumns.tsx
index 995eaa5636..53bb702175 100644
--- a/src/containers/Nodes/getNodesColumns.tsx
+++ b/src/containers/Nodes/getNodesColumns.tsx
@@ -17,6 +17,8 @@ import {
formatStorageValuesToGb,
} from '../../utils/dataFormatters/dataFormatters';
+export const NODES_COLUMNS_WIDTH_LS_KEY = 'nodesTableColumnsWidth';
+
const NODES_COLUMNS_IDS = {
NodeId: 'NodeId',
Host: 'Host',
@@ -127,6 +129,7 @@ const cpuColumn: NodesColumn = {
render: ({row}) => (row.PoolStats ? : '—'),
align: DataTable.LEFT,
width: 80,
+ resizeMinWidth: 60,
sortable: false,
};
@@ -149,12 +152,14 @@ const loadAverageColumn: NodesColumn = {
),
align: DataTable.LEFT,
width: 140,
+ resizeMinWidth: 140,
sortable: false,
};
const getTabletsColumn = (tabletsPath?: string): NodesColumn => ({
name: NODES_COLUMNS_IDS.Tablets,
- width: 430,
+ width: 500,
+ resizeMinWidth: 500,
render: ({row}) => {
return row.Tablets ? (
&
DataTableColumn;
@@ -71,6 +73,7 @@ const typeColumn: StorageGroupsColumn = {
name: GROUPS_COLUMNS_IDS.MediaType,
header: 'Type',
width: 100,
+ resizeMinWidth: 100,
align: DataTable.LEFT,
render: ({row}) => (
@@ -119,6 +122,7 @@ const usageColumn: StorageGroupsColumn = {
name: GROUPS_COLUMNS_IDS.Usage,
header: 'Usage',
width: 100,
+ resizeMinWidth: 70,
render: ({row}) => {
// without a limit the usage can be evaluated as 0,
// but the absence of a value is more clear
@@ -209,7 +213,7 @@ const writeColumn: StorageGroupsColumn = {
align: DataTable.RIGHT,
};
-const getVdiscksColumn = (nodes?: NodesMap): StorageGroupsColumn => ({
+const getVDisksColumn = (nodes?: NodesMap): StorageGroupsColumn => ({
name: GROUPS_COLUMNS_IDS.VDisks,
className: b('vdisks-column'),
header: 'VDisks',
@@ -229,6 +233,7 @@ const getVdiscksColumn = (nodes?: NodesMap): StorageGroupsColumn => ({
),
align: DataTable.CENTER,
width: 900,
+ resizeable: false,
});
export const getStorageTopGroupsColumns = (): StorageGroupsColumn[] => {
@@ -244,7 +249,7 @@ export const getPDiskStorageColumns = (nodes?: NodesMap): StorageGroupsColumn[]
groupIdColumn,
usageColumn,
usedColumn,
- getVdiscksColumn(nodes),
+ getVDisksColumn(nodes),
];
};
@@ -261,7 +266,7 @@ const getStorageGroupsColumns = (nodes?: NodesMap): StorageGroupsColumn[] => {
usedSpaceFlagColumn,
readColumn,
writeColumn,
- getVdiscksColumn(nodes),
+ getVDisksColumn(nodes),
];
};
diff --git a/src/containers/Storage/StorageNodes/StorageNodes.tsx b/src/containers/Storage/StorageNodes/StorageNodes.tsx
index dac1e81567..0062915c41 100644
--- a/src/containers/Storage/StorageNodes/StorageNodes.tsx
+++ b/src/containers/Storage/StorageNodes/StorageNodes.tsx
@@ -1,6 +1,6 @@
import type {Settings, SortOrder} from '@gravity-ui/react-data-table';
-import DataTable from '@gravity-ui/react-data-table';
+import {ResizeableDataTable} from '../../../components/ResizeableDataTable/ResizeableDataTable';
import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants';
import type {PreparedStorageNode, VisibleEntities} from '../../../store/reducers/storage/types';
import type {AdditionalNodesProps} from '../../../types/additionalProps';
@@ -8,7 +8,10 @@ import type {HandleSort} from '../../../utils/hooks/useTableSort';
import {NodesUptimeFilterValues} from '../../../utils/nodes';
import {StorageNodesEmptyDataMessage} from './StorageNodesEmptyDataMessage';
-import {getPreparedStorageNodesColumns} from './getStorageNodesColumns';
+import {
+ STORAGE_NODES_COLUMNS_WIDTH_LS_KEY,
+ getPreparedStorageNodesColumns,
+} from './getStorageNodesColumns';
import i18n from './i18n';
import {getRowUnavailableClassName} from './shared';
@@ -52,9 +55,9 @@ export function StorageNodes({
}
return (
- [] = [
{
@@ -43,15 +45,12 @@ const columns: Column[] = [
{
name: 'Node FQDN',
sortable: false,
+ width: 300,
render: ({row}) => {
if (!row.fqdn) {
return —;
}
- return (
-
-
-
- );
+ return ;
},
},
];
@@ -66,8 +65,8 @@ interface TabletTableProps {
export const TabletTable = ({history}: TabletTableProps) => {
return (
- {
}
return (
- {
-
[] = [
{
name: CONSUMERS_COLUMNS_IDS.CONSUMER,
@@ -53,6 +55,7 @@ export const columns: Column[] = [
name: CONSUMERS_COLUMNS_IDS.READ_SPEED,
header: CONSUMERS_COLUMNS_TITILES[CONSUMERS_COLUMNS_IDS.READ_SPEED],
align: DataTable.RIGHT,
+ resizeMinWidth: 140,
sortAccessor: (row) => row.readSpeed.perMinute,
render: ({row}) => ,
},
diff --git a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.scss b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.scss
index 1aac9acd9a..05a10cfa8d 100644
--- a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.scss
+++ b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.scss
@@ -1,11 +1,7 @@
@import '../../../../styles/mixins.scss';
.ydb-hot-keys {
- &__table-content {
- overflow: auto;
-
- width: 100%;
- height: 100%;
+ &__table {
@include freeze-nth-column(1);
}
diff --git a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx
index f9aaf57f77..dbabd542d6 100644
--- a/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx
+++ b/src/containers/Tenant/Diagnostics/HotKeys/HotKeys.tsx
@@ -5,6 +5,7 @@ import type {Column} from '@gravity-ui/react-data-table';
import {ResponseError} from '../../../../components/Errors/ResponseError';
import {Icon} from '../../../../components/Icon';
+import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {
setHotKeysData,
setHotKeysDataWasNotLoaded,
@@ -129,17 +130,15 @@ export function HotKeys({path}: HotKeysProps) {
}
return (
-
-
-
+
);
}
diff --git a/src/containers/Tenant/Diagnostics/Partitions/Partitions.scss b/src/containers/Tenant/Diagnostics/Partitions/Partitions.scss
index 5a08ba102a..713a7f98f6 100644
--- a/src/containers/Tenant/Diagnostics/Partitions/Partitions.scss
+++ b/src/containers/Tenant/Diagnostics/Partitions/Partitions.scss
@@ -39,7 +39,9 @@
overflow: auto;
height: 100%;
+ }
+ &__table {
@include freeze-nth-column(1);
}
}
diff --git a/src/containers/Tenant/Diagnostics/Partitions/Partitions.tsx b/src/containers/Tenant/Diagnostics/Partitions/Partitions.tsx
index 34a8bd659b..07ace11a11 100644
--- a/src/containers/Tenant/Diagnostics/Partitions/Partitions.tsx
+++ b/src/containers/Tenant/Diagnostics/Partitions/Partitions.tsx
@@ -1,9 +1,9 @@
import React from 'react';
-import DataTable from '@gravity-ui/react-data-table';
import {skipToken} from '@reduxjs/toolkit/query';
import {ResponseError} from '../../../../components/Errors/ResponseError';
+import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton';
import {nodesListApi, selectNodesMap} from '../../../../store/reducers/nodesList';
import {partitionsApi, setSelectedConsumer} from '../../../../store/reducers/partitions/partitions';
@@ -13,6 +13,7 @@ import {DEFAULT_TABLE_SETTINGS, PARTITIONS_HIDDEN_COLUMNS_KEY} from '../../../..
import {useSetting, useTypedDispatch, useTypedSelector} from '../../../../utils/hooks';
import {PartitionsControls} from './PartitionsControls/PartitionsControls';
+import {PARTITIONS_COLUMNS_WIDTH_LS_KEY} from './columns';
import i18n from './i18n';
import {addHostToPartitions} from './utils';
import type {PreparedPartitionDataWithHosts} from './utils/types';
@@ -106,7 +107,23 @@ export const Partitions = ({path}: PartitionsProps) => {
const error = nodesError || topicError || partitionsError;
- const getContent = () => {
+ const renderControls = () => {
+ return (
+
+ );
+ };
+
+ const renderContent = () => {
if (loading) {
return ;
}
@@ -115,8 +132,9 @@ export const Partitions = ({path}: PartitionsProps) => {
}
return (
- {
return (
-
+
{renderControls()}
-
{getContent()}
+
{renderContent()}
);
diff --git a/src/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx b/src/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx
index 3348ebb9fc..25e70bd5cf 100644
--- a/src/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx
+++ b/src/containers/Tenant/Diagnostics/Partitions/PartitionsControls/PartitionsControls.tsx
@@ -146,7 +146,7 @@ export const PartitionsControls = ({
};
return (
-
+
-
+
);
};
diff --git a/src/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss b/src/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss
index 5e0753f4be..bd6fc14e28 100644
--- a/src/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss
+++ b/src/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss
@@ -2,12 +2,4 @@
&__lags-header {
text-align: center;
}
-
- &__string-with-copy {
- overflow: hidden;
-
- width: 150px;
-
- text-overflow: ellipsis;
- }
}
diff --git a/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx b/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx
index a076a983b4..0a9f677d9d 100644
--- a/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx
+++ b/src/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx
@@ -30,6 +30,8 @@ import './Columns.scss';
const b = cn('ydb-diagnostics-partitions-columns');
+export const PARTITIONS_COLUMNS_WIDTH_LS_KEY = 'partitionsColumnsWidth';
+
export const allColumns: Column[] = [
{
name: PARTITIONS_COLUMNS_IDS.PARTITION_ID,
@@ -54,6 +56,7 @@ export const allColumns: Column[] = [
name: PARTITIONS_COLUMNS_IDS.WRITE_SPEED,
header: PARTITIONS_COLUMNS_TITLES[PARTITIONS_COLUMNS_IDS.WRITE_SPEED],
align: DataTable.LEFT,
+ resizeMinWidth: 140,
sortAccessor: (row) => row.writeSpeed.perMinute,
render: ({row}) => ,
},
@@ -61,6 +64,7 @@ export const allColumns: Column[] = [
name: PARTITIONS_COLUMNS_IDS.READ_SPEED,
header: PARTITIONS_COLUMNS_TITLES[PARTITIONS_COLUMNS_IDS.READ_SPEED],
align: DataTable.LEFT,
+ resizeMinWidth: 140,
sortAccessor: (row) => row.readSpeed?.perMinute,
render: ({row}) => ,
},
@@ -165,14 +169,10 @@ export const allColumns: Column[] = [
name: PARTITIONS_COLUMNS_IDS.READ_SESSION_ID,
header: ,
align: DataTable.LEFT,
+ width: 150,
render: ({row}) =>
row.readSessionId ? (
-
+
) : (
'–'
),
@@ -185,14 +185,10 @@ export const allColumns: Column[] = [
/>
),
align: DataTable.LEFT,
+ width: 150,
render: ({row}) =>
row.readerName ? (
-
+
) : (
'–'
),
@@ -205,6 +201,7 @@ export const allColumns: Column[] = [
/>
),
align: DataTable.LEFT,
+ width: 200,
render: ({row}) =>
row.partitionNodeId && row.partitionHost ? (
[] = [
path={getDefaultNodePath(row.partitionNodeId)}
showStatus={false}
hasClipboardButton
- className={b('string-with-copy')}
/>
) : (
'–'
@@ -226,6 +222,7 @@ export const allColumns: Column[] = [
/>
),
align: DataTable.LEFT,
+ width: 200,
render: ({row}) =>
row.connectionNodeId && row.connectionHost ? (
[] = [
path={getDefaultNodePath(row.connectionNodeId)}
showStatus={false}
hasClipboardButton
- className={b('string-with-copy')}
/>
) : (
'–'
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx
index ea639d90b7..1a1e0c6763 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopNodesByCpu.tsx
@@ -2,7 +2,10 @@ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/
import {topNodesApi} from '../../../../../store/reducers/tenantOverview/topNodes/topNodes';
import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
import {useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
-import {getTopNodesByCpuColumns} from '../../../../Nodes/getNodesColumns';
+import {
+ NODES_COLUMNS_WIDTH_LS_KEY,
+ getTopNodesByCpuColumns,
+} from '../../../../Nodes/getNodesColumns';
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
import {getSectionTitle} from '../getSectionTitle';
@@ -38,6 +41,7 @@ export function TopNodesByCpu({path, additionalNodesProps}: TopNodesByCpuProps)
return (
b('top-queries-row')}
/>
);
}
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx
index c9c50dda62..544f2fdb25 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx
@@ -5,7 +5,10 @@ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/
import {topShardsApi} from '../../../../../store/reducers/tenantOverview/topShards/tenantOverviewTopShards';
import {useTypedSelector} from '../../../../../utils/hooks';
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
-import {getTopShardsColumns} from '../../TopShards/getTopShardsColumns';
+import {
+ TOP_SHARDS_COLUMNS_WIDTH_LS_KEY,
+ getTopShardsColumns,
+} from '../../TopShards/getTopShardsColumns';
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
import {getSectionTitle} from '../getSectionTitle';
import i18n from '../i18n';
@@ -42,12 +45,12 @@ export const TopShards = ({path}: TopShardsProps) => {
return (
);
};
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx
index 0233aea8ef..5c8796648d 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantMemory/TopNodesByMemory.tsx
@@ -2,7 +2,10 @@ import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/
import {topNodesApi} from '../../../../../store/reducers/tenantOverview/topNodes/topNodes';
import type {AdditionalNodesProps} from '../../../../../types/additionalProps';
import {useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
-import {getTopNodesByMemoryColumns} from '../../../../Nodes/getNodesColumns';
+import {
+ NODES_COLUMNS_WIDTH_LS_KEY,
+ getTopNodesByMemoryColumns,
+} from '../../../../Nodes/getNodesColumns';
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
import {getSectionTitle} from '../getSectionTitle';
@@ -40,6 +43,7 @@ export function TopNodesByMemory({path, additionalNodesProps}: TopNodesByMemoryP
return (
extends Omit, 'theme'> {
+interface TenantOverviewTableLayoutProps extends ResizeableDataTableProps {
title: React.ReactNode;
loading?: boolean;
error?: unknown;
@@ -37,9 +36,7 @@ export function TenantOverviewTableLayout({
return ;
}
- return (
-
- );
+ return ;
};
return (
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx
index d23786f055..2dfbea04c7 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx
@@ -1,7 +1,10 @@
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
import {topStorageGroupsApi} from '../../../../../store/reducers/tenantOverview/topStorageGroups/topStorageGroups';
import {useSearchQuery, useTypedSelector} from '../../../../../utils/hooks';
-import {getStorageTopGroupsColumns} from '../../../../Storage/StorageGroups/getStorageGroupsColumns';
+import {
+ STORAGE_GROUPS_COLUMNS_WIDTH_LS_KEY,
+ getStorageTopGroupsColumns,
+} from '../../../../Storage/StorageGroups/getStorageGroupsColumns';
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
import {getSectionTitle} from '../getSectionTitle';
@@ -36,6 +39,7 @@ export function TopGroups({tenant}: TopGroupsProps) {
return (
[] = [
{
name: 'Size',
- width: 80,
+ width: 100,
sortable: false,
render: ({row}) => formatSize(Number(row.Size)),
align: DataTable.RIGHT,
},
{
name: 'Path',
+ width: 700,
sortable: false,
render: ({row}) =>
row.Path ? (
@@ -65,6 +68,7 @@ export function TopTables({path}: TopTablesProps) {
return (
{
dispatch(setTopQueriesFilters(value));
};
- const renderLoader = () => {
- return (
-
-
-
- );
- };
-
const renderContent = () => {
- if (loading) {
- return renderLoader();
- }
-
if (error && typeof error === 'object' && !(error as any).isCancelled) {
return {prepareQueryError(error)}
;
}
@@ -112,21 +100,20 @@ export const TopQueries = ({path, type}: TopQueriesProps) => {
}
return (
-
-
-
+ b('row')}
+ />
);
};
- return (
-
-
+ const renderControls = () => {
+ return (
+
{
className={b('search')}
/>
-
- {renderContent()}
-
+
+ );
+ };
+
+ return (
+
+ {renderControls()}
+
+ {renderContent()}
+
+
);
};
diff --git a/src/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx b/src/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx
index e6edebdf83..675c3b403b 100644
--- a/src/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx
+++ b/src/containers/Tenant/Diagnostics/TopQueries/getTopQueriesColumns.tsx
@@ -16,6 +16,8 @@ import './TopQueries.scss';
const b = cn('kv-top-queries');
+export const TOP_QUERIES_COLUMNS_WIDTH_LS_KEY = 'topQueriesColumnsWidth';
+
const TOP_QUERIES_COLUMNS_IDS = {
CPUTimeUs: 'CPUTimeUs',
QueryText: 'QueryText',
@@ -45,12 +47,14 @@ const queryTextColumn: Column = {
),
sortable: false,
+ width: 500,
};
const endTimeColumn: Column
= {
name: TOP_QUERIES_COLUMNS_IDS.EndTime,
render: ({row}) => formatDateTime(new Date(row.EndTime as string).getTime()),
align: DataTable.RIGHT,
+ width: 200,
};
const readRowsColumn: Column = {
@@ -58,6 +62,7 @@ const readRowsColumn: Column = {
render: ({row}) => formatNumber(row.ReadRows),
sortAccessor: (row) => Number(row.ReadRows),
align: DataTable.RIGHT,
+ width: 150,
};
const readBytesColumn: Column = {
@@ -65,6 +70,7 @@ const readBytesColumn: Column = {
render: ({row}) => formatNumber(row.ReadBytes),
sortAccessor: (row) => Number(row.ReadBytes),
align: DataTable.RIGHT,
+ width: 150,
};
const userSIDColumn: Column = {
@@ -79,6 +85,7 @@ const oneLineQueryTextColumn: Column = {
header: 'QueryText',
render: ({row}) => ,
sortable: false,
+ width: 500,
};
const queryHashColumn: Column = {
@@ -94,6 +101,7 @@ const durationColumn: Column = {
render: ({row}) => formatNumber(parseUsToMs(row.Duration ?? undefined)),
sortAccessor: (row) => Number(row.Duration),
align: DataTable.RIGHT,
+ width: 150,
};
export const getTopQueriesColumns = (): Column[] => {
diff --git a/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss b/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss
deleted file mode 100644
index 9b6202ff4f..0000000000
--- a/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.top-shards {
- &__filters {
- display: flex;
- flex-wrap: wrap;
- align-items: baseline;
- gap: 16px;
- }
-}
diff --git a/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx b/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx
index c0b25a2ac2..f99cd17a55 100644
--- a/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx
+++ b/src/containers/Tenant/Diagnostics/TopShards/Filters/Filters.tsx
@@ -1,3 +1,5 @@
+import React from 'react';
+
import {RadioButton} from '@gravity-ui/uikit';
import type {DateRangeValues} from '../../../../../components/DateRange';
@@ -5,18 +7,15 @@ import {DateRange} from '../../../../../components/DateRange';
import {EShardsWorkloadMode} from '../../../../../store/reducers/shardsWorkload/types';
import type {ShardsWorkloadFilters} from '../../../../../store/reducers/shardsWorkload/types';
import {isEnumMember} from '../../../../../utils/typecheckers';
-import {b} from '../TopShards';
import i18n from '../i18n';
-import './Filters.scss';
-
interface FiltersProps {
value: ShardsWorkloadFilters;
onChange: (value: Partial) => void;
className?: string;
}
-export const Filters = ({value, onChange, className}: FiltersProps) => {
+export const Filters = ({value, onChange}: FiltersProps) => {
const handleModeChange = (mode: string) => {
if (!isEnumMember(EShardsWorkloadMode, mode)) {
const values = Object.values(EShardsWorkloadMode).join(', ');
@@ -37,7 +36,7 @@ export const Filters = ({value, onChange, className}: FiltersProps) => {
const to = value.mode === EShardsWorkloadMode.Immediate ? undefined : value.to;
return (
-
+
{i18n('filters.mode.immediate')}
@@ -47,6 +46,6 @@ export const Filters = ({value, onChange, className}: FiltersProps) => {
-
+
);
};
diff --git a/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss b/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss
index 49c0ad7d1c..53f76c2eaa 100644
--- a/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss
+++ b/src/containers/Tenant/Diagnostics/TopShards/TopShards.scss
@@ -1,19 +1,8 @@
.top-shards {
- display: flex;
- flex-direction: column;
- gap: 10px;
+ &__hint {
+ position: sticky;
+ left: 0;
- height: 100%;
-
- background-color: var(--g-color-base-background);
-
- &__loader {
- display: flex;
- justify-content: center;
- }
-
- &__table {
- overflow: auto;
- flex-grow: 1;
+ width: max-content;
}
}
diff --git a/src/containers/Tenant/Diagnostics/TopShards/TopShards.tsx b/src/containers/Tenant/Diagnostics/TopShards/TopShards.tsx
index 3561b63857..60a21c7b81 100644
--- a/src/containers/Tenant/Diagnostics/TopShards/TopShards.tsx
+++ b/src/containers/Tenant/Diagnostics/TopShards/TopShards.tsx
@@ -2,9 +2,10 @@ import React from 'react';
import type {Column, Settings, SortOrder} from '@gravity-ui/react-data-table';
import DataTable from '@gravity-ui/react-data-table';
-import {Loader} from '@gravity-ui/uikit';
import {useLocation} from 'react-router';
+import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
+import {TableWithControlsLayout} from '../../../../components/TableWithControlsLayout/TableWithControlsLayout';
import {
setShardsQueryFilters,
shardApi,
@@ -22,12 +23,12 @@ import {prepareQueryError} from '../../../../utils/query';
import {isColumnEntityType} from '../../utils/schema';
import {Filters} from './Filters';
-import {getShardsWorkloadColumns} from './getTopShardsColumns';
+import {TOP_SHARDS_COLUMNS_WIDTH_LS_KEY, getShardsWorkloadColumns} from './getTopShardsColumns';
import i18n from './i18n';
import './TopShards.scss';
-export const b = cn('top-shards');
+const b = cn('top-shards');
const TABLE_SETTINGS: Settings = {
...DEFAULT_TABLE_SETTINGS,
@@ -186,19 +187,11 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
return columns;
}, [filters.mode, tenantPath, location]);
- const renderLoader = () => {
- return (
-
-
-
- );
+ const renderControls = () => {
+ return ;
};
const renderContent = () => {
- if (loading) {
- return renderLoader();
- }
-
if (error && typeof error === 'object' && !(error as any).isCancelled) {
return {prepareQueryError(error)}
;
}
@@ -208,24 +201,28 @@ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
}
return (
-
-
-
+
);
};
return (
-
-
- {filters.mode === EShardsWorkloadMode.History &&
{i18n('description')}
}
- {renderContent()}
-
+
+ {renderControls()}
+
+ {filters.mode === EShardsWorkloadMode.History && (
+ {i18n('description')}
+ )}
+
+
+ {renderContent()}
+
+
);
};
diff --git a/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx b/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx
index dac7a58ef1..fb81d4aefe 100644
--- a/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx
+++ b/src/containers/Tenant/Diagnostics/TopShards/getTopShardsColumns.tsx
@@ -12,6 +12,8 @@ import type {ValueOf} from '../../../../types/common';
import {formatNumber, roundToPrecision} from '../../../../utils/dataFormatters/dataFormatters';
import {getDefaultNodePath} from '../../../Node/NodePages';
+export const TOP_SHARDS_COLUMNS_WIDTH_LS_KEY = 'topShardsColumnsWidth';
+
const TOP_SHARDS_COLUMNS_IDS = {
TabletId: 'TabletId',
CPUCores: 'CPUCores',
@@ -52,6 +54,7 @@ const getPathColumn = (schemaPath: string, location: Location): Column = {
@@ -86,6 +89,7 @@ const tabletIdColumn: Column = {
);
},
sortable: false,
+ width: 190,
};
const nodeIdColumn: Column = {
@@ -113,6 +117,8 @@ const topShardsCpuCoresColumn: Column = {
},
align: DataTable.RIGHT,
sortable: false,
+ width: 140,
+ resizeMinWidth: 140,
};
const inFlightTxCountColumn: Column = {
diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.scss b/src/containers/Tenant/ObjectSummary/ObjectSummary.scss
index 4d49f6395b..009d7d7fcd 100644
--- a/src/containers/Tenant/ObjectSummary/ObjectSummary.scss
+++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.scss
@@ -77,12 +77,6 @@
flex-direction: column;
}
- &__schema {
- display: flex;
- overflow: auto;
- flex-grow: 1;
- }
-
&__info-controls {
display: flex;
gap: 4px;
diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx
index e6dfb9c653..47fe45967c 100644
--- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx
+++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx
@@ -193,9 +193,7 @@ export function ObjectSummary({
return ;
}
case TENANT_SUMMARY_TABS_IDS.schema: {
- return (
-
- );
+ return ;
}
default: {
return renderObjectOverview();
diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
index f9f2050da4..09524fe411 100644
--- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
+++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
@@ -7,7 +7,7 @@
flex-grow: 1;
flex-direction: column;
- padding: 0px 10px;
+ padding-left: 10px;
@include query-data-table();
& .data-table__table-wrapper {
@@ -20,8 +20,6 @@
flex-direction: column;
width: 100%;
- margin-top: 10px;
- padding: 0 10px 10px;
}
&__result-tabs {
@@ -59,7 +57,6 @@
gap: 4px;
}
&__inspector {
- max-width: calc(100% - 50px);
padding: 15px 10px;
@include json-tree-styles();
&_fullscreen {
@@ -70,21 +67,4 @@
padding: 10px;
}
}
-
- &__fullscreen-table-wrapper {
- overflow: auto;
-
- width: 100%;
- height: 100%;
- margin: 20px;
-
- background-color: var(--g-color-base-background);
- @include table-styles();
- table {
- width: 100% !important;
- }
- .data-table__table-wrapper {
- padding: 0 !important;
- }
- }
}
diff --git a/src/containers/Tenant/Query/Preview/Preview.scss b/src/containers/Tenant/Query/Preview/Preview.scss
index cbdb4c5964..955f9a1ba5 100644
--- a/src/containers/Tenant/Query/Preview/Preview.scss
+++ b/src/containers/Tenant/Query/Preview/Preview.scss
@@ -1,8 +1,9 @@
-@import '../../../../styles/mixins.scss';
+@use '../../../../styles/mixins.scss';
.kv-preview {
height: 100%;
- @include query-data-table;
+ @include mixins.flex-container();
+ @include mixins.query-data-table();
&__header {
position: sticky;
@@ -48,9 +49,11 @@
&__result {
overflow: auto;
+ width: 100%;
+
// This fixes last row display for ordinary preview (not fullscreen)
height: calc(100% - 40px);
- padding: 0 10px;
+ padding-left: 10px;
// Fix white space footer block for fullscreen preview
.kv-fullscreen & {
diff --git a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss
index 13447ab35e..5cc905049b 100644
--- a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss
+++ b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.scss
@@ -7,7 +7,6 @@
padding: 0 16px;
@include flex-container();
- @include table-styles;
&__table-row {
cursor: pointer;
diff --git a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx
index 627e4cfa99..e3a840e76b 100644
--- a/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx
+++ b/src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx
@@ -1,6 +1,6 @@
import type {Column} from '@gravity-ui/react-data-table';
-import DataTable from '@gravity-ui/react-data-table';
+import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
import {selectQueriesHistory} from '../../../../store/reducers/executeQuery';
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
@@ -16,6 +16,8 @@ import './QueriesHistory.scss';
const b = cn('ydb-queries-history');
+const QUERIES_HISTORY_COLUMNS_WIDTH_LS_KEY = 'queriesHistoryTableColumnsWidth';
+
interface QueriesHistoryProps {
changeUserInput: (value: {input: string}) => void;
}
@@ -52,6 +54,7 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
);
},
sortable: false,
+ width: 600,
},
{
name: 'syntax',
@@ -66,8 +69,8 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
return (
- void;
@@ -117,14 +120,15 @@ export const SavedQueries = ({savedQueries, changeUserInput, onDeleteQuery}: Sav
),
sortable: false,
+ resizeMinWidth: 650,
},
];
return (
-
{
+export const SchemaViewer = ({type, path, withFamilies = false}: SchemaViewerProps) => {
const {data, loading} = useTypedSelector((state) => state.schema);
const currentObjectData = path ? data[path] : undefined;
@@ -32,12 +33,12 @@ export const SchemaViewer = ({className, type, path, withFamilies = false}: Sche
const families = prepareFamilies(currentObjectData);
return (
-
+
{loading ? (
) : (
-
[] = [
{
name: SchemaViewerColumns.id,
- width: 40,
+ width: 60,
},
];
@@ -122,7 +124,8 @@ export function prepareSchemaTableColumns(options: {
// External tables don't have key columns
columns.push({
name: SchemaViewerColumns.key,
- width: 40,
+ width: 70,
+ resizeMinWidth: 70,
// Table should start with key columns on sort click
defaultOrder: DataTable.ASCENDING,
// Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns
diff --git a/src/containers/Tenants/Tenants.scss b/src/containers/Tenants/Tenants.scss
index 86646f74bc..aa313ef345 100644
--- a/src/containers/Tenants/Tenants.scss
+++ b/src/containers/Tenants/Tenants.scss
@@ -25,20 +25,28 @@
}
}
- &__table {
- @include table-styles;
+ &__type {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 10px;
}
&__type-value {
- margin-right: 10px;
+ overflow: hidden;
+
+ width: min-content;
+
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
&__type-button {
- visibility: hidden;
+ display: none;
}
.data-table__row:hover &__type-button {
- visibility: visible;
+ display: block;
}
&__name-wrapper {
diff --git a/src/containers/Tenants/Tenants.tsx b/src/containers/Tenants/Tenants.tsx
index 9a8ed7af0f..203791482d 100644
--- a/src/containers/Tenants/Tenants.tsx
+++ b/src/containers/Tenants/Tenants.tsx
@@ -10,6 +10,7 @@ import {ResponseError} from '../../components/Errors/ResponseError';
import {Illustration} from '../../components/Illustration';
import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
import {ProblemFilter} from '../../components/ProblemFilter';
+import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
import {Search} from '../../components/Search';
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
import {TabletsStatistic} from '../../components/TabletsStatistic';
@@ -41,6 +42,8 @@ import './Tenants.scss';
const b = cn('tenants');
+const DATABASES_COLUMNS_WIDTH_LS_KEY = 'databasesTableColumnsWidth';
+
interface TenantsProps {
additionalTenantsProps?: AdditionalTenantsProps;
}
@@ -135,6 +138,7 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
{
name: 'Type',
width: 200,
+ resizeMinWidth: 150,
render: ({row}) => {
if (row.Type !== 'Serverless') {
return row.Type;
@@ -209,6 +213,7 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
name: 'PoolStats',
header: 'Pools',
width: 100,
+ resizeMinWidth: 60,
sortAccessor: ({PoolStats = []}) =>
PoolStats.reduce((acc, item) => acc + (item.Usage || 0), 0),
defaultOrder: DataTable.DESCENDING,
@@ -219,7 +224,8 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
name: 'Tablets',
header: 'Tablets States',
sortable: false,
- width: 430,
+ width: 500,
+ resizeMinWidth: 500,
render: ({row}) => {
const backend = getTenantBackend(row);
@@ -242,8 +248,8 @@ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
}
return (
- {
return (
{renderControls()}
-
+
{renderTable()}
diff --git a/src/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss b/src/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss
index ea599b954d..9af922b4e8 100644
--- a/src/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss
+++ b/src/containers/Versions/GroupedNodesTree/GroupedNodesTree.scss
@@ -23,8 +23,6 @@
@include freeze-nth-column(1);
@include freeze-nth-column(2, 80px);
-
- @include table-styles;
}
.ydb-tree-view {
diff --git a/src/containers/Versions/NodesTable/NodesTable.tsx b/src/containers/Versions/NodesTable/NodesTable.tsx
index e0c8f38129..bcb8928241 100644
--- a/src/containers/Versions/NodesTable/NodesTable.tsx
+++ b/src/containers/Versions/NodesTable/NodesTable.tsx
@@ -4,17 +4,21 @@ import DataTable from '@gravity-ui/react-data-table';
import {EntityStatus} from '../../../components/EntityStatus/EntityStatus';
import {PoolsGraph} from '../../../components/PoolsGraph/PoolsGraph';
import {ProgressViewer} from '../../../components/ProgressViewer/ProgressViewer';
+import {ResizeableDataTable} from '../../../components/ResizeableDataTable/ResizeableDataTable';
import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/clusterNodes';
import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants';
import {formatBytes} from '../../../utils/dataFormatters/dataFormatters';
import {isUnavailableNode} from '../../../utils/nodes';
import {getDefaultNodePath} from '../../Node/NodePages';
+const VERSIONS_COLUMNS_WIDTH_LS_KEY = 'versionsTableColumnsWidth';
+
const columns: Column[] = [
{
name: 'NodeId',
header: '#',
- width: '80px',
+ width: 80,
+ resizeMinWidth: 80,
align: DataTable.LEFT,
render: ({row}) => row.NodeId,
},
@@ -33,7 +37,7 @@ const columns: Column[] = [
);
},
- width: '400px',
+ width: 400,
align: DataTable.LEFT,
},
{
@@ -43,14 +47,14 @@ const columns: Column[] = [
row.Endpoints
? row.Endpoints.map(({Name, Address}) => `${Name} ${Address}`).join(', ')
: '-',
- width: '300px',
+ width: 300,
align: DataTable.LEFT,
},
{
name: 'uptime',
header: 'Uptime',
sortAccessor: ({StartTime}) => StartTime && -StartTime,
- width: '120px',
+ width: 120,
align: DataTable.LEFT,
render: ({row}) => row.uptime,
},
@@ -60,7 +64,7 @@ const columns: Column[] = [
sortAccessor: ({MemoryUsed = 0}) => Number(MemoryUsed),
defaultOrder: DataTable.DESCENDING,
render: ({row}) => (row.MemoryUsed ? formatBytes(row.MemoryUsed) : '—'),
- width: '120px',
+ width: 120,
align: DataTable.RIGHT,
},
{
@@ -69,7 +73,7 @@ const columns: Column[] = [
sortAccessor: ({MemoryLimit = 0}) => Number(MemoryLimit),
defaultOrder: DataTable.DESCENDING,
render: ({row}) => (row.MemoryLimit ? formatBytes(row.MemoryLimit) : '—'),
- width: '120px',
+ width: 120,
align: DataTable.RIGHT,
},
{
@@ -78,7 +82,8 @@ const columns: Column[] = [
sortAccessor: ({PoolStats = []}) =>
PoolStats.reduce((acc, item) => acc + (item.Usage || 0), 0),
defaultOrder: DataTable.DESCENDING,
- width: '120px',
+ width: 80,
+ resizeMinWidth: 60,
render: ({row}) => (row.PoolStats ? : '—'),
align: DataTable.LEFT,
},
@@ -88,7 +93,8 @@ const columns: Column[] = [
sortAccessor: ({LoadAverage = []}) =>
LoadAverage.slice(0, 1).reduce((acc, item) => acc + item, 0),
defaultOrder: DataTable.DESCENDING,
- width: '200px',
+ width: 140,
+ resizeMinWidth: 140,
render: ({row}) =>
row.LoadAverage && row.LoadAverage.length > 0 ? (
{
return (
- = VirtualTableColumn & DataTableColumn;
-
-export type TableColumnsWidthSetup = Record;
-
-export type HandleTableColumnsResize = (newSetup: TableColumnsWidthSetup) => void;
-
-export const updateColumnsWidth = (
- columns: Column[],
- columnsWidthSetup: TableColumnsWidthSetup,
-) => {
- return columns.map((column) => {
- const resizeable = column.resizeable ?? DEFAULT_RESIZEABLE;
-
- if (!resizeable) {
- return column;
+export const useTableResize = (localStorageKey?: string): [ColumnWidthByName, HandleResize] => {
+ const getSizes: GetSavedColumnWidthByName = React.useCallback(() => {
+ if (!localStorageKey) {
+ return {};
}
- return {...column, width: columnsWidthSetup[column.name] ?? column.width};
- });
-};
-
-export const useTableResize = (
- localStorageKey: string,
-): [TableColumnsWidthSetup, HandleTableColumnsResize] => {
- const [tableColumnsWidthSetup, setTableColumnsWidth] = React.useState(
- () => {
- const setupFromLS = settingsManager.readUserSettingsValue(
- localStorageKey,
- {},
- ) as TableColumnsWidthSetup;
-
- return setupFromLS;
- },
- );
-
- const handleSetupChange: HandleTableColumnsResize = React.useCallback(
- (newSetup) => {
- setTableColumnsWidth((previousSetup) => {
- // ResizeObserver callback may be triggered only for currently resized column
- // or for the whole set of columns
- const setup = {
- ...previousSetup,
- ...newSetup,
- };
- settingsManager.setUserSettingsValue(localStorageKey, setup);
- return setup;
- });
+ return settingsManager.readUserSettingsValue(localStorageKey, {}) as ColumnWidthByName;
+ }, [localStorageKey]);
+
+ const saveSizes: SaveColumnWidthByName = React.useCallback(
+ (value) => {
+ if (localStorageKey) {
+ settingsManager.setUserSettingsValue(localStorageKey, value);
+ }
},
[localStorageKey],
);
- return [tableColumnsWidthSetup, handleSetupChange];
+ return libUseTableResize({saveSizes, getSizes});
};