ydb-embedded-ui 3.3.4 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +19 -0
- package/dist/components/Errors/ResponseError/ResponseError.tsx +2 -2
- package/dist/components/InfoViewer/formatters/topicStats.tsx +8 -29
- package/dist/components/LabelWithPopover/LabelWithPopover.tsx +20 -0
- package/dist/components/LabelWithPopover/index.ts +1 -0
- package/dist/components/LagImages/LagImages.tsx +205 -0
- package/dist/components/LagImages/index.ts +1 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.scss +92 -0
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +120 -0
- package/dist/components/SpeedMultiMeter/i18n/en.json +6 -0
- package/dist/components/SpeedMultiMeter/i18n/index.ts +13 -0
- package/dist/components/SpeedMultiMeter/i18n/ru.json +6 -0
- package/dist/components/SpeedMultiMeter/index.ts +1 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +18 -14
- package/dist/containers/Storage/VDisk/VDisk.tsx +20 -5
- package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +34 -5
- package/dist/containers/Storage/utils/types.ts +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.scss +32 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +62 -69
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/Headers.tsx +27 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.scss +32 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/ConsumersTopicStats.tsx +43 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/TopicStats/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/Columns.scss +5 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/columns.tsx +66 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/en.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/i18n/ru.json +4 -3
- package/dist/containers/Tenant/Diagnostics/Consumers/utils/constants.ts +23 -0
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -0
- package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +8 -2
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +9 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +6 -8
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.scss +33 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/Headers.tsx +76 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Headers/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.scss +45 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/Partitions.tsx +254 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/PartitionsWrapper.tsx +79 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/Columns.scss +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/columns.tsx +246 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/columns/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/en.json +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/index.ts +11 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/i18n/ru.json +13 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/index.ts +1 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/constants.ts +74 -0
- package/dist/containers/Tenant/Diagnostics/Partitions/utils/types.ts +6 -0
- package/dist/containers/Tenant/utils/schema.ts +1 -16
- package/dist/services/api.d.ts +4 -0
- package/dist/services/api.js +22 -6
- package/dist/store/reducers/consumer.ts +160 -0
- package/dist/store/reducers/index.ts +2 -0
- package/dist/store/reducers/settings.js +2 -0
- package/dist/store/reducers/topic.ts +82 -2
- package/dist/types/store/consumer.ts +55 -0
- package/dist/types/store/topic.ts +23 -6
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +24 -0
- package/dist/utils/bytesParsers/formatBytesCustom.ts +57 -0
- package/dist/utils/bytesParsers/i18n/en.json +7 -0
- package/dist/utils/bytesParsers/i18n/index.ts +11 -0
- package/dist/utils/bytesParsers/i18n/ru.json +7 -0
- package/dist/utils/bytesParsers/index.ts +2 -0
- package/dist/utils/constants.ts +3 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/storage.ts +2 -2
- package/dist/utils/timeParsers/index.ts +2 -1
- package/dist/utils/timeParsers/parsers.ts +18 -0
- package/dist/utils/timeParsers/{protobuf.ts → protobufParsers.ts} +0 -0
- package/dist/utils/utils.js +3 -3
- package/package.json +2 -2
@@ -1,16 +1,28 @@
|
|
1
|
+
/* eslint-disable camelcase */
|
1
2
|
import type {Reducer} from 'redux';
|
3
|
+
import {createSelector, Selector} from 'reselect';
|
2
4
|
|
3
|
-
import type {
|
5
|
+
import type {
|
6
|
+
IPreparedConsumerData,
|
7
|
+
IPreparedTopicStats,
|
8
|
+
ITopicAction,
|
9
|
+
ITopicRootStateSlice,
|
10
|
+
ITopicState,
|
11
|
+
} from '../../types/store/topic';
|
4
12
|
import '../../services/api';
|
5
13
|
|
6
14
|
import {createRequestActionTypes, createApiRequest} from '../utils';
|
15
|
+
import {parseLag, parseTimestampToIdleTime} from '../../utils/timeParsers';
|
16
|
+
import {convertBytesObjectToSpeed} from '../../utils/bytesParsers';
|
7
17
|
|
8
18
|
export const FETCH_TOPIC = createRequestActionTypes('topic', 'FETCH_TOPIC');
|
9
19
|
|
20
|
+
const SET_DATA_WAS_NOT_LOADED = 'topic/SET_DATA_WAS_NOT_LOADED';
|
21
|
+
|
10
22
|
const initialState = {
|
11
23
|
loading: true,
|
12
24
|
wasLoaded: false,
|
13
|
-
data:
|
25
|
+
data: undefined,
|
14
26
|
};
|
15
27
|
|
16
28
|
const topic: Reducer<ITopicState, ITopicAction> = (state = initialState, action) => {
|
@@ -22,6 +34,11 @@ const topic: Reducer<ITopicState, ITopicAction> = (state = initialState, action)
|
|
22
34
|
};
|
23
35
|
}
|
24
36
|
case FETCH_TOPIC.SUCCESS: {
|
37
|
+
// On older version it can return HTML page of Internal Viewer with an error
|
38
|
+
if (typeof action.data !== 'object') {
|
39
|
+
return {...state, loading: false, error: {}};
|
40
|
+
}
|
41
|
+
|
25
42
|
return {
|
26
43
|
...state,
|
27
44
|
data: action.data,
|
@@ -31,17 +48,33 @@ const topic: Reducer<ITopicState, ITopicAction> = (state = initialState, action)
|
|
31
48
|
};
|
32
49
|
}
|
33
50
|
case FETCH_TOPIC.FAILURE: {
|
51
|
+
if (action.error?.isCancelled) {
|
52
|
+
return state;
|
53
|
+
}
|
54
|
+
|
34
55
|
return {
|
35
56
|
...state,
|
36
57
|
error: action.error,
|
37
58
|
loading: false,
|
38
59
|
};
|
39
60
|
}
|
61
|
+
case SET_DATA_WAS_NOT_LOADED: {
|
62
|
+
return {
|
63
|
+
...state,
|
64
|
+
wasLoaded: false,
|
65
|
+
};
|
66
|
+
}
|
40
67
|
default:
|
41
68
|
return state;
|
42
69
|
}
|
43
70
|
};
|
44
71
|
|
72
|
+
export const setDataWasNotLoaded = () => {
|
73
|
+
return {
|
74
|
+
type: SET_DATA_WAS_NOT_LOADED,
|
75
|
+
} as const;
|
76
|
+
};
|
77
|
+
|
45
78
|
export function getTopic(path?: string) {
|
46
79
|
return createApiRequest({
|
47
80
|
request: window.api.getTopic({path}),
|
@@ -49,4 +82,51 @@ export function getTopic(path?: string) {
|
|
49
82
|
});
|
50
83
|
}
|
51
84
|
|
85
|
+
const selectTopicStats = (state: ITopicRootStateSlice) => state.topic.data?.topic_stats;
|
86
|
+
const selectConsumers = (state: ITopicRootStateSlice) => state.topic.data?.consumers;
|
87
|
+
|
88
|
+
export const selectPreparedTopicStats: Selector<
|
89
|
+
ITopicRootStateSlice,
|
90
|
+
IPreparedTopicStats | undefined
|
91
|
+
> = createSelector([selectTopicStats], (rawTopicStats) => {
|
92
|
+
if (!rawTopicStats) {
|
93
|
+
return undefined;
|
94
|
+
}
|
95
|
+
|
96
|
+
const {
|
97
|
+
store_size_bytes = '0',
|
98
|
+
min_last_write_time,
|
99
|
+
max_write_time_lag,
|
100
|
+
bytes_written,
|
101
|
+
} = rawTopicStats || {};
|
102
|
+
|
103
|
+
return {
|
104
|
+
storeSize: store_size_bytes,
|
105
|
+
partitionsIdleTime: parseTimestampToIdleTime(min_last_write_time),
|
106
|
+
partitionsWriteLag: parseLag(max_write_time_lag),
|
107
|
+
writeSpeed: convertBytesObjectToSpeed(bytes_written),
|
108
|
+
};
|
109
|
+
});
|
110
|
+
|
111
|
+
export const selectPreparedConsumersData: Selector<
|
112
|
+
ITopicRootStateSlice,
|
113
|
+
IPreparedConsumerData[] | undefined
|
114
|
+
> = createSelector([selectConsumers], (consumers) => {
|
115
|
+
return consumers?.map((consumer) => {
|
116
|
+
const {name, consumer_stats} = consumer || {};
|
117
|
+
|
118
|
+
const {min_partitions_last_read_time, max_read_time_lag, max_write_time_lag, bytes_read} =
|
119
|
+
consumer_stats || {};
|
120
|
+
|
121
|
+
return {
|
122
|
+
name,
|
123
|
+
readSpeed: convertBytesObjectToSpeed(bytes_read),
|
124
|
+
|
125
|
+
writeLag: parseLag(max_write_time_lag),
|
126
|
+
readLag: parseLag(max_read_time_lag),
|
127
|
+
readIdleTime: parseTimestampToIdleTime(min_partitions_last_read_time),
|
128
|
+
};
|
129
|
+
});
|
130
|
+
});
|
131
|
+
|
52
132
|
export default topic;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import type {IProcessSpeedStats} from '../../utils/bytesParsers';
|
2
|
+
import type {ApiRequestAction} from '../../store/utils';
|
3
|
+
|
4
|
+
import {FETCH_CONSUMER, setDataWasNotLoaded} from '../../store/reducers/consumer';
|
5
|
+
|
6
|
+
import type {DescribeConsumerResult} from '../api/consumer';
|
7
|
+
import type {IResponseError} from '../api/error';
|
8
|
+
|
9
|
+
// All fields should be present though they could be undefined
|
10
|
+
export interface IPreparedPartitionData {
|
11
|
+
partitionId: string;
|
12
|
+
storeSize: string;
|
13
|
+
|
14
|
+
writeSpeed: IProcessSpeedStats;
|
15
|
+
readSpeed: IProcessSpeedStats;
|
16
|
+
|
17
|
+
partitionWriteLag: number;
|
18
|
+
partitionWriteIdleTime: number;
|
19
|
+
|
20
|
+
consumerWriteLag: number;
|
21
|
+
consumerReadLag: number;
|
22
|
+
consumerReadIdleTime: number;
|
23
|
+
|
24
|
+
uncommitedMessages: number;
|
25
|
+
unreadMessages: number;
|
26
|
+
|
27
|
+
startOffset: string;
|
28
|
+
endOffset: string;
|
29
|
+
commitedOffset: string;
|
30
|
+
|
31
|
+
readSessionId: string | undefined;
|
32
|
+
readerName: string | undefined;
|
33
|
+
|
34
|
+
partitionNodeId: number;
|
35
|
+
connectionNodeId: number;
|
36
|
+
}
|
37
|
+
|
38
|
+
export interface IConsumerState {
|
39
|
+
loading: boolean;
|
40
|
+
wasLoaded: boolean;
|
41
|
+
data?: DescribeConsumerResult;
|
42
|
+
error?: IResponseError;
|
43
|
+
}
|
44
|
+
|
45
|
+
type IConsumerApiRequestAction = ApiRequestAction<
|
46
|
+
typeof FETCH_CONSUMER,
|
47
|
+
DescribeConsumerResult,
|
48
|
+
IResponseError
|
49
|
+
>;
|
50
|
+
|
51
|
+
export type IConsumerAction = IConsumerApiRequestAction | ReturnType<typeof setDataWasNotLoaded>;
|
52
|
+
|
53
|
+
export interface IConsumerRootStateSlice {
|
54
|
+
consumer: IConsumerState;
|
55
|
+
}
|
@@ -1,8 +1,27 @@
|
|
1
|
-
import {FETCH_TOPIC} from '../../store/reducers/topic';
|
1
|
+
import {FETCH_TOPIC, setDataWasNotLoaded} from '../../store/reducers/topic';
|
2
2
|
import type {ApiRequestAction} from '../../store/utils';
|
3
|
+
import type {IProcessSpeedStats} from '../../utils/bytesParsers';
|
3
4
|
import type {IResponseError} from '../api/error';
|
4
5
|
import type {DescribeTopicResult} from '../api/topic';
|
5
6
|
|
7
|
+
export interface IPreparedConsumerData {
|
8
|
+
name: string | undefined;
|
9
|
+
readSpeed: IProcessSpeedStats;
|
10
|
+
|
11
|
+
writeLag: number;
|
12
|
+
readLag: number;
|
13
|
+
readIdleTime: number;
|
14
|
+
}
|
15
|
+
|
16
|
+
export interface IPreparedTopicStats {
|
17
|
+
storeSize: string;
|
18
|
+
|
19
|
+
partitionsWriteLag: number;
|
20
|
+
partitionsIdleTime: number;
|
21
|
+
|
22
|
+
writeSpeed: IProcessSpeedStats;
|
23
|
+
}
|
24
|
+
|
6
25
|
export interface ITopicState {
|
7
26
|
loading: boolean;
|
8
27
|
wasLoaded: boolean;
|
@@ -10,11 +29,9 @@ export interface ITopicState {
|
|
10
29
|
error?: IResponseError;
|
11
30
|
}
|
12
31
|
|
13
|
-
export type ITopicAction =
|
14
|
-
typeof FETCH_TOPIC,
|
15
|
-
|
16
|
-
IResponseError
|
17
|
-
>;
|
32
|
+
export type ITopicAction =
|
33
|
+
| ApiRequestAction<typeof FETCH_TOPIC, DescribeTopicResult, IResponseError>
|
34
|
+
| ReturnType<typeof setDataWasNotLoaded>;
|
18
35
|
|
19
36
|
export interface ITopicRootStateSlice {
|
20
37
|
topic: ITopicState;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import type {MultipleWindowsStat} from '../../types/api/consumer';
|
2
|
+
|
3
|
+
import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from '../constants';
|
4
|
+
|
5
|
+
export interface IProcessSpeedStats {
|
6
|
+
perMinute: number;
|
7
|
+
perHour: number;
|
8
|
+
perDay: number;
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Convert data of type MultipleWindowsStat.
|
13
|
+
* This data format is specific for describe_topic and describe_consumer endpoints
|
14
|
+
*/
|
15
|
+
export const convertBytesObjectToSpeed = (
|
16
|
+
data: MultipleWindowsStat | undefined,
|
17
|
+
): IProcessSpeedStats => {
|
18
|
+
return {
|
19
|
+
perMinute:
|
20
|
+
data && data.per_minute ? Math.round(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,
|
21
|
+
perHour: data && data.per_hour ? Math.round(Number(data.per_hour) / HOUR_IN_SECONDS) : 0,
|
22
|
+
perDay: data && data.per_day ? Math.round(Number(data.per_day) / DAY_IN_SECONDS) : 0,
|
23
|
+
};
|
24
|
+
};
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import {GIGABYTE, KILOBYTE, MEGABYTE} from '../constants';
|
2
|
+
import {isNumeric} from '../utils';
|
3
|
+
|
4
|
+
import i18n from './i18n';
|
5
|
+
|
6
|
+
const sizes = {
|
7
|
+
b: {
|
8
|
+
value: 1,
|
9
|
+
label: i18n('b'),
|
10
|
+
},
|
11
|
+
kb: {
|
12
|
+
value: KILOBYTE,
|
13
|
+
label: i18n('kb'),
|
14
|
+
},
|
15
|
+
mb: {
|
16
|
+
value: MEGABYTE,
|
17
|
+
label: i18n('mb'),
|
18
|
+
},
|
19
|
+
gb: {
|
20
|
+
value: GIGABYTE,
|
21
|
+
label: i18n('gb'),
|
22
|
+
},
|
23
|
+
};
|
24
|
+
|
25
|
+
export type IBytesSizes = keyof typeof sizes;
|
26
|
+
|
27
|
+
interface FormatBytesArgs {
|
28
|
+
value: number | string | undefined;
|
29
|
+
size?: IBytesSizes;
|
30
|
+
precision?: number;
|
31
|
+
withLabel?: boolean;
|
32
|
+
isSpeed?: boolean;
|
33
|
+
}
|
34
|
+
|
35
|
+
export const formatBytesCustom = ({
|
36
|
+
value,
|
37
|
+
size = 'mb',
|
38
|
+
precision = 0,
|
39
|
+
withLabel = true,
|
40
|
+
isSpeed = false,
|
41
|
+
}: FormatBytesArgs) => {
|
42
|
+
if (!isNumeric(value)) {
|
43
|
+
return '';
|
44
|
+
}
|
45
|
+
|
46
|
+
let result = (Number(value) / sizes[size].value).toFixed(precision);
|
47
|
+
|
48
|
+
if (withLabel) {
|
49
|
+
result += ` ${sizes[size].label}`;
|
50
|
+
|
51
|
+
if (isSpeed) {
|
52
|
+
result += i18n('perSecond');
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
return result;
|
57
|
+
};
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import {i18n, Lang} from '../../i18n';
|
2
|
+
|
3
|
+
import en from './en.json';
|
4
|
+
import ru from './ru.json';
|
5
|
+
|
6
|
+
const COMPONENT = 'ydb-bytes-parsers';
|
7
|
+
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
10
|
+
|
11
|
+
export default i18n.keyset(COMPONENT);
|
package/dist/utils/constants.ts
CHANGED
@@ -9,6 +9,7 @@ export const AUTO_RELOAD_INTERVAL = 10 * SECOND;
|
|
9
9
|
// by agreement, display all byte values in decimal scale
|
10
10
|
// values in data are always in bytes, never in higher units,
|
11
11
|
// therefore there is no issue arbitrary converting them in UI
|
12
|
+
export const KILOBYTE = 1_000;
|
12
13
|
export const MEGABYTE = 1_000_000;
|
13
14
|
export const GIGABYTE = 1_000_000_000;
|
14
15
|
export const TERABYTE = 1_000_000_000_000;
|
@@ -118,3 +119,5 @@ export const DEFAULT_TABLE_SETTINGS = {
|
|
118
119
|
|
119
120
|
export const TENANT_INITIAL_TAB_KEY = 'saved_tenant_initial_tab';
|
120
121
|
export const QUERY_INITIAL_RUN_ACTION_KEY = 'query_initial_run_action';
|
122
|
+
|
123
|
+
export const PARTITIONS_SELECTED_COLUMNS_KEY = 'partitionsSelectedColumns';
|
package/dist/utils/index.js
CHANGED
@@ -9,6 +9,8 @@ import {isNumeric} from './utils';
|
|
9
9
|
|
10
10
|
numeral.locale(i18n.lang);
|
11
11
|
|
12
|
+
// Here you can't control displayed size and precision
|
13
|
+
// If you need more custom format, use formatBytesCustom instead
|
12
14
|
export const formatBytes = (bytes) => {
|
13
15
|
if (!isNumeric(bytes)) {
|
14
16
|
return '';
|
@@ -42,6 +44,10 @@ export const formatUptime = (seconds) => {
|
|
42
44
|
return uptime;
|
43
45
|
};
|
44
46
|
|
47
|
+
export const formatMsToUptime = (ms) => {
|
48
|
+
return formatUptime(ms / 1000);
|
49
|
+
};
|
50
|
+
|
45
51
|
export const formatIOPS = (value, capacity) => {
|
46
52
|
return [Math.floor(value), Math.floor(capacity) + ' IOPS'];
|
47
53
|
};
|
package/dist/utils/storage.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import type {TVSlotId, TVDiskStateInfo} from '../types/api/vdisk';
|
2
2
|
import type {IStoragePoolGroup} from '../types/store/storage';
|
3
3
|
|
4
|
-
export const
|
5
|
-
'VDiskId' in
|
4
|
+
export const isFullVDiksData = (disk: TVDiskStateInfo | TVSlotId): disk is TVDiskStateInfo =>
|
5
|
+
'VDiskId' in disk;
|
6
6
|
|
7
7
|
export const getUsage = (data: IStoragePoolGroup, step = 1) => {
|
8
8
|
// if limit is 0, display 0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import type {IProtobufTimeObject} from '../../types/api/common';
|
2
|
+
|
3
|
+
import {parseProtobufDurationToMs, parseProtobufTimestampToMs} from '.';
|
4
|
+
|
5
|
+
export const parseLag = (value: string | IProtobufTimeObject | undefined) =>
|
6
|
+
value ? parseProtobufDurationToMs(value) : 0;
|
7
|
+
|
8
|
+
export const parseTimestampToIdleTime = (value: string | IProtobufTimeObject | undefined) => {
|
9
|
+
if (!value) {
|
10
|
+
return 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
const duration = Date.now() - parseProtobufTimestampToMs(value);
|
14
|
+
|
15
|
+
// Duration could be negative because of the difference between server and local time
|
16
|
+
// Usually it below 100ms, so it could be omitted
|
17
|
+
return duration < 0 ? 0 : duration;
|
18
|
+
};
|
File without changes
|
package/dist/utils/utils.js
CHANGED
@@ -76,9 +76,9 @@ export function bytesToGB(bytes, shouldRound) {
|
|
76
76
|
|
77
77
|
export function pad9(val) {
|
78
78
|
const len = String(val).length;
|
79
|
-
let result = val
|
79
|
+
let result = val;
|
80
80
|
for (let i = len; i < 9; i++) {
|
81
|
-
result =
|
81
|
+
result = '0' + result;
|
82
82
|
}
|
83
83
|
return result;
|
84
84
|
}
|
@@ -88,4 +88,4 @@ export function isNumeric(value) {
|
|
88
88
|
// - isNaN treats true/false/''/etc. as numbers, parseFloat fixes this
|
89
89
|
// - parseFloat treats '123qwe' as number, isNaN fixes this
|
90
90
|
return !isNaN(value) && !isNaN(parseFloat(value));
|
91
|
-
}
|
91
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "ydb-embedded-ui",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.4.0",
|
4
4
|
"files": [
|
5
5
|
"dist"
|
6
6
|
],
|
@@ -39,7 +39,7 @@
|
|
39
39
|
"reselect": "4.1.6",
|
40
40
|
"sass": "1.32.8",
|
41
41
|
"web-vitals": "1.1.2",
|
42
|
-
"ydb-ui-components": "^3.0.
|
42
|
+
"ydb-ui-components": "^3.0.3"
|
43
43
|
},
|
44
44
|
"scripts": {
|
45
45
|
"start": "react-app-rewired start",
|