Skip to content

Commit 13d21cb

Browse files
authored
feat: display all types of keys as plain text (#1219)
1 parent 3191b31 commit 13d21cb

File tree

16 files changed

+344
-84
lines changed

16 files changed

+344
-84
lines changed

src/components/ContentWithPopup/ContentWithPopup.tsx

+14-1
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,20 @@ interface ContentWithPopupProps extends PopupProps {
77
content: React.ReactNode;
88
className?: string;
99
children?: React.ReactNode;
10+
pinOnClick?: boolean;
1011
}
1112

1213
export const ContentWithPopup = ({
1314
children,
1415
content,
1516
className,
17+
pinOnClick,
1618
hasArrow = true,
1719
placement = ['top', 'bottom'],
1820
...props
1921
}: ContentWithPopupProps) => {
2022
const [isPopupVisible, setIsPopupVisible] = React.useState(false);
23+
const [isPinned, setIsPinned] = React.useState(false);
2124
const anchor = React.useRef(null);
2225

2326
const showPopup = () => {
@@ -28,20 +31,30 @@ export const ContentWithPopup = ({
2831
setIsPopupVisible(false);
2932
};
3033

34+
const pinPopup = () => {
35+
setIsPinned(true);
36+
};
37+
38+
const unpinPopup = () => {
39+
setIsPinned(false);
40+
};
41+
3142
return (
3243
<React.Fragment>
3344
<Popup
3445
anchorRef={anchor}
35-
open={isPopupVisible}
46+
open={isPinned || isPopupVisible}
3647
placement={placement}
3748
hasArrow={hasArrow}
49+
onOutsideClick={unpinPopup}
3850
{...props}
3951
>
4052
{content}
4153
</Popup>
4254
<span
4355
className={className}
4456
ref={anchor}
57+
onClick={pinOnClick ? pinPopup : undefined}
4558
onMouseEnter={showPopup}
4659
onMouseLeave={hidePopup}
4760
>

src/containers/Tenant/ObjectSummary/ObjectSummary.scss

+3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
max-height: 100%;
1414

1515
&__overview-wrapper {
16+
position: relative;
17+
1618
display: flex;
1719
overflow: auto;
1820
flex-grow: 1;
21+
flex-direction: column;
1922

2023
padding: 0 12px 16px;
2124
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {Label} from '@gravity-ui/uikit';
2+
3+
import {ContentWithPopup} from '../../../../components/ContentWithPopup/ContentWithPopup';
4+
5+
import i18n from './i18n';
6+
import {b} from './shared';
7+
import type {SchemaData} from './types';
8+
import {getPartitioningKeys, getPrimaryKeys} from './utils';
9+
10+
const MAX_VISIBLE_KEYS = 3;
11+
12+
interface KeysViewProps {
13+
tableData: SchemaData[];
14+
extended?: boolean;
15+
type: 'primary' | 'partitioning';
16+
}
17+
18+
export const KeysView = ({tableData, extended, type}: KeysViewProps) => {
19+
const keys = type === 'primary' ? getPrimaryKeys(tableData) : getPartitioningKeys(tableData);
20+
21+
const visibleCount = extended ? MAX_VISIBLE_KEYS : keys.length;
22+
const visibleKeys = keys.slice(0, visibleCount);
23+
const hiddenKeys = keys.slice(visibleCount);
24+
25+
return keys.length > 0 ? (
26+
<div className={b('keys', {summary: !extended, type})}>
27+
<div className={b('keys-header')}>
28+
{type === 'primary' ? i18n('primary-key.title') : i18n('partitioning-key.title')}
29+
</div>
30+
<div className={b('keys-values')}>
31+
{' ' + visibleKeys.join(', ')}
32+
{hiddenKeys.length ? (
33+
<ContentWithPopup
34+
className={b('more-badge')}
35+
placement={['bottom']}
36+
hasArrow={false}
37+
pinOnClick
38+
content={
39+
<div className={b('popup-content')}>
40+
{hiddenKeys.map((key) => (
41+
<div className={b('popup-item')} key={key}>
42+
{key}
43+
</div>
44+
))}
45+
</div>
46+
}
47+
>
48+
<Label className={b('keys-label')}>{`+${hiddenKeys.length}`}</Label>
49+
</ContentWithPopup>
50+
) : null}
51+
</div>
52+
</div>
53+
) : null;
54+
};
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,65 @@
1+
@import '../../../../styles/mixins.scss';
2+
13
.schema-viewer {
2-
&__key-icon {
3-
display: flex;
4-
align-items: center;
4+
&__keys {
5+
display: inline-block;
6+
7+
padding-bottom: var(--g-spacing-4);
8+
padding-left: 10px;
9+
10+
&-values {
11+
display: inline;
12+
13+
color: var(--g-color-text-complementary);
14+
@include body-1-typography();
15+
}
16+
17+
&-header {
18+
display: inline;
19+
20+
font-weight: bold;
21+
white-space: nowrap;
22+
23+
color: var(--g-color-text-primary);
24+
@include subheader-1-typography();
25+
}
26+
27+
&-label {
28+
cursor: pointer;
29+
}
30+
31+
&-wrapper {
32+
position: sticky;
33+
z-index: 1;
34+
left: 0;
35+
36+
width: 100%;
37+
}
38+
39+
& + & {
40+
margin-left: var(--g-spacing-8);
41+
}
42+
43+
&_summary {
44+
& + & {
45+
margin-left: 0;
46+
}
47+
}
48+
}
49+
50+
&__popup-content {
51+
padding: var(--g-spacing-2) var(--g-spacing-4);
52+
}
53+
54+
&__popup-item {
55+
padding-bottom: var(--g-spacing-2);
56+
57+
&:last-child {
58+
padding-bottom: 0;
59+
}
60+
}
61+
62+
&__more-badge {
63+
margin-left: var(--g-spacing-1);
564
}
665
}

src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx

+21-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react';
22

3-
import DataTable from '@gravity-ui/react-data-table';
43
import {skipToken} from '@reduxjs/toolkit/query';
54

65
import {ResizeableDataTable} from '../../../../components/ResizeableDataTable/ResizeableDataTable';
@@ -16,9 +15,9 @@ import {
1615
isViewType,
1716
} from '../../utils/schema';
1817

18+
import {KeysView} from './KeysView';
1919
import {
2020
SCHEMA_COLUMNS_WIDTH_LS_KEY,
21-
SCHEMA_TABLE_COLUMS_IDS,
2221
getColumnTableColumns,
2322
getExternalTableColumns,
2423
getRowTableColumns,
@@ -77,24 +76,24 @@ export const SchemaViewer = ({type, path, tenantName, extended = false}: SchemaV
7776
return [];
7877
}, [type, extended, hasAutoIncrement, hasDefaultValue]);
7978

80-
const renderContent = () => {
81-
if (loading || isViewSchemaLoading) {
82-
return <TableSkeleton />;
83-
}
84-
85-
return (
86-
<ResizeableDataTable
87-
columnsWidthLSKey={SCHEMA_COLUMNS_WIDTH_LS_KEY}
88-
data={tableData}
89-
columns={columns}
90-
settings={DEFAULT_TABLE_SETTINGS}
91-
initialSortOrder={{
92-
columnId: SCHEMA_TABLE_COLUMS_IDS.isKeyColumn,
93-
order: DataTable.ASCENDING,
94-
}}
95-
/>
96-
);
97-
};
98-
99-
return <div className={b(null)}>{renderContent()}</div>;
79+
if (loading || isViewSchemaLoading) {
80+
return <TableSkeleton />;
81+
}
82+
83+
return (
84+
<React.Fragment>
85+
<div className={b('keys-wrapper')}>
86+
<KeysView tableData={tableData} extended={extended} type="primary" />
87+
<KeysView tableData={tableData} extended={extended} type="partitioning" />
88+
</div>
89+
<div className={b()}>
90+
<ResizeableDataTable
91+
columnsWidthLSKey={SCHEMA_COLUMNS_WIDTH_LS_KEY}
92+
data={tableData}
93+
columns={columns}
94+
settings={DEFAULT_TABLE_SETTINGS}
95+
/>
96+
</div>
97+
</React.Fragment>
98+
);
10099
};

src/containers/Tenant/Schema/SchemaViewer/columns.tsx

+2-24
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import DataTable from '@gravity-ui/react-data-table';
2-
import {Icon} from '@gravity-ui/uikit';
32

43
import i18n from './i18n';
5-
import {b} from './shared';
64
import type {SchemaColumn, SchemaData} from './types';
75

8-
import keyIcon from '../../../../assets/icons/key.svg';
9-
106
export const SCHEMA_COLUMNS_WIDTH_LS_KEY = 'schemaTableColumnsWidth';
117

128
export const SCHEMA_TABLE_COLUMS_IDS = {
@@ -38,24 +34,6 @@ const nameColumn: SchemaColumn = {
3834
width: 100,
3935
render: ({row}) => row.name,
4036
};
41-
const keyColumn: SchemaColumn = {
42-
name: SCHEMA_TABLE_COLUMS_IDS.isKeyColumn,
43-
get header() {
44-
return i18n('column-title.key');
45-
},
46-
width: 70,
47-
resizeMinWidth: 70,
48-
// Table should start with key columns on sort click
49-
defaultOrder: DataTable.ASCENDING,
50-
sortAccessor: (row) => row.keyAccessor,
51-
render: ({row}) => {
52-
return row.isKeyColumn ? (
53-
<div className={b('key-icon')}>
54-
<Icon data={keyIcon} width={12} height={7} />
55-
</div>
56-
) : null;
57-
},
58-
};
5937
const typeColumn: SchemaColumn = {
6038
name: SCHEMA_TABLE_COLUMS_IDS.type,
6139
get header() {
@@ -137,14 +115,14 @@ export function getExternalTableColumns(): SchemaColumn[] {
137115
return [idColumn, nameColumn, typeColumn, notNullColumn];
138116
}
139117
export function getColumnTableColumns(): SchemaColumn[] {
140-
return [idColumn, keyColumn, nameColumn, typeColumn, notNullColumn];
118+
return [idColumn, nameColumn, typeColumn, notNullColumn];
141119
}
142120
export function getRowTableColumns(
143121
extended: boolean,
144122
hasAutoIncrement: boolean,
145123
hasDefaultValue: boolean,
146124
): SchemaColumn[] {
147-
const rowTableColumns = [idColumn, keyColumn, nameColumn, typeColumn, notNullColumn];
125+
const rowTableColumns = [idColumn, nameColumn, typeColumn, notNullColumn];
148126

149127
if (hasDefaultValue) {
150128
rowTableColumns.push(defaultValueColumn);
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
{
22
"column-title.id": "Id",
33
"column-title.name": "Name",
4-
"column-title.key": "Key",
54
"column-title.type": "Type",
65
"column-title.notNull": "NotNull",
76
"column-title.autoIncrement": "AutoIncrement",
87
"column-title.defaultValue": "Default",
98
"column-title.family": "Family",
109
"column-title.media": "Media",
11-
"column-title.compression": "Compression"
10+
"column-title.compression": "Compression",
11+
"primary-key.title": "Primary key:",
12+
"partitioning-key.title": "Partitioning key:"
1213
}

0 commit comments

Comments
 (0)