ydb-embedded-ui 3.5.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
- package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
- package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
- package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
- package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
- package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
- package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
- package/dist/containers/UserSettings/UserSettings.tsx +30 -1
- package/dist/services/api.d.ts +4 -3
- package/dist/services/api.js +2 -2
- package/dist/store/reducers/executeQuery.ts +12 -37
- package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
- package/dist/store/reducers/settings.js +18 -3
- package/dist/types/api/error.ts +14 -0
- package/dist/types/api/query.ts +226 -117
- package/dist/types/store/executeQuery.ts +4 -8
- package/dist/types/store/explainQuery.ts +38 -0
- package/dist/types/store/query.ts +23 -3
- package/dist/utils/constants.ts +2 -1
- package/dist/utils/error.ts +25 -0
- package/dist/utils/index.js +0 -49
- package/dist/utils/prepareQueryExplain.ts +7 -24
- package/dist/utils/query.test.ts +153 -231
- package/dist/utils/query.ts +44 -78
- package/dist/utils/timeParsers/i18n/en.json +9 -9
- package/dist/utils/timeParsers/i18n/ru.json +9 -9
- package/dist/utils/timeParsers/parsers.ts +9 -0
- package/dist/utils/utils.js +1 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [4.0.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.5.0...v4.0.0) (2023-04-28)
|
4
|
+
|
5
|
+
|
6
|
+
### ⚠ BREAKING CHANGES
|
7
|
+
|
8
|
+
* app no longer parses query responses from older ydb versions
|
9
|
+
* v0.1 explain plans are no longer rendered
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* enable explain-script parsing, remove deprecated code ([5c6e9a2](https://github.com/ydb-platform/ydb-embedded-ui/commit/5c6e9a21026ea9eb3e32650e6fdda89c7900e7e6))
|
14
|
+
* **QueryEditor:** add explain query modes ([39ad943](https://github.com/ydb-platform/ydb-embedded-ui/commit/39ad9434c1622e22901e6cc1af1568e0edf6b434))
|
15
|
+
* **QueryEditor:** display query duration ([967f102](https://github.com/ydb-platform/ydb-embedded-ui/commit/967f10296d2362709654172ed7318509286efc78))
|
16
|
+
* remove support for explain v0.1 ([c8741a6](https://github.com/ydb-platform/ydb-embedded-ui/commit/c8741a69b82053185a07c7ba563455d4f28ecdce))
|
17
|
+
|
18
|
+
|
19
|
+
### Bug Fixes
|
20
|
+
|
21
|
+
* **query:** correctly process NetworkError on actions failure ([cf5bd6c](https://github.com/ydb-platform/ydb-embedded-ui/commit/cf5bd6c5c4c2972fec93b2dc9135c92c639fa5f9))
|
22
|
+
* **QueryExplain:** do not request ast when missing ([54cf151](https://github.com/ydb-platform/ydb-embedded-ui/commit/54cf151452e17256173736450f5727085ea591ff))
|
23
|
+
* **QueryExplain:** request AST if it is empty ([d028b4e](https://github.com/ydb-platform/ydb-embedded-ui/commit/d028b4ed08a98281baff81683204f1cbc1c20c37))
|
24
|
+
|
3
25
|
## [3.5.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v3.4.5...v3.5.0) (2023-04-18)
|
4
26
|
|
5
27
|
|
@@ -2,8 +2,9 @@ import React, {useRef, useState} from 'react';
|
|
2
2
|
import cn from 'bem-cn-lite';
|
3
3
|
import _ from 'lodash';
|
4
4
|
import {Button, Popup} from '@gravity-ui/uikit';
|
5
|
+
|
5
6
|
import TruncatedQuery from '../../../../components/TruncatedQuery/TruncatedQuery';
|
6
|
-
import {
|
7
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
7
8
|
|
8
9
|
import './QueriesHistory.scss';
|
9
10
|
|
@@ -17,8 +18,7 @@ interface QueriesHistoryProps {
|
|
17
18
|
|
18
19
|
function QueriesHistory(props: QueriesHistoryProps) {
|
19
20
|
const [isHistoryVisible, setIsHistoryVisible] = useState(false);
|
20
|
-
const history
|
21
|
-
useSelector((state: any) => state.executeQuery.history?.queries) ?? [];
|
21
|
+
const history = useTypedSelector((state) => state.executeQuery.history.queries) ?? [];
|
22
22
|
const anchor = useRef(null);
|
23
23
|
|
24
24
|
const onShowHistoryClick = () => {
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {formatDurationToShortTimeFormat, parseUsToMs} from '../../../../utils/timeParsers';
|
4
|
+
|
5
|
+
import './QueryDuration.scss';
|
6
|
+
|
7
|
+
interface QueryDurationProps {
|
8
|
+
duration?: string;
|
9
|
+
}
|
10
|
+
|
11
|
+
const b = block('ydb-query-duration');
|
12
|
+
|
13
|
+
export const QueryDuration = ({duration}: QueryDurationProps) => {
|
14
|
+
if (!duration) {
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
|
18
|
+
const parsedDuration = formatDurationToShortTimeFormat(parseUsToMs(duration), 1);
|
19
|
+
|
20
|
+
return <span className={b()}>{parsedDuration}</span>;
|
21
|
+
};
|
@@ -4,41 +4,36 @@ import {connect} from 'react-redux';
|
|
4
4
|
import cn from 'bem-cn-lite';
|
5
5
|
import _ from 'lodash';
|
6
6
|
import MonacoEditor from 'react-monaco-editor';
|
7
|
-
import {Button, DropdownMenu} from '@gravity-ui/uikit';
|
8
7
|
|
9
8
|
import SplitPane from '../../../components/SplitPane';
|
10
9
|
import {QueryResultTable} from '../../../components/QueryResultTable';
|
11
10
|
|
12
|
-
import SaveQuery from './SaveQuery/SaveQuery';
|
13
11
|
import SavedQueries from './SavedQueries/SavedQueries';
|
14
|
-
import {Icon} from '../../../components/Icon';
|
15
12
|
import QueryResult from './QueryResult/QueryResult';
|
16
13
|
import QueryExplain from './QueryExplain/QueryExplain';
|
14
|
+
import {QueryEditorControls} from './QueryEditorControls/QueryEditorControls';
|
15
|
+
import {OldQueryEditorControls} from './QueryEditorControls/OldQueryEditorControls';
|
17
16
|
|
18
17
|
import {
|
19
|
-
|
18
|
+
sendExecuteQuery,
|
20
19
|
changeUserInput,
|
21
20
|
saveQueryToHistory,
|
22
21
|
goToPreviousQuery,
|
23
22
|
goToNextQuery,
|
24
|
-
selectRunAction,
|
25
|
-
RUN_ACTIONS_VALUES,
|
26
23
|
MONACO_HOT_KEY_ACTIONS,
|
27
24
|
setMonacoHotKey,
|
28
25
|
} from '../../../store/reducers/executeQuery';
|
29
26
|
import {getExplainQuery, getExplainQueryAst} from '../../../store/reducers/explainQuery';
|
30
|
-
import {
|
27
|
+
import {getParsedSettingValue, setSettingValue} from '../../../store/reducers/settings';
|
31
28
|
import {
|
32
29
|
DEFAULT_IS_QUERY_RESULT_COLLAPSED,
|
33
30
|
DEFAULT_SIZE_RESULT_PANE_KEY,
|
34
31
|
SAVED_QUERIES_KEY,
|
35
|
-
|
32
|
+
QUERY_INITIAL_MODE_KEY,
|
33
|
+
ENABLE_QUERY_MODES_FOR_EXPLAIN,
|
36
34
|
} from '../../../utils/constants';
|
37
35
|
|
38
|
-
import {parseJson} from '../../../utils/utils';
|
39
|
-
|
40
36
|
import './QueryEditor.scss';
|
41
|
-
import Divider from '../../../components/Divider/Divider';
|
42
37
|
import QueriesHistory from './QueriesHistory/QueriesHistory';
|
43
38
|
import {
|
44
39
|
PaneVisibilityActionTypes,
|
@@ -47,11 +42,6 @@ import {
|
|
47
42
|
import Preview from '../Preview/Preview';
|
48
43
|
import {setShowPreview} from '../../../store/reducers/schema';
|
49
44
|
|
50
|
-
export const RUN_ACTIONS = [
|
51
|
-
{value: RUN_ACTIONS_VALUES.script, content: 'Run Script'},
|
52
|
-
{value: RUN_ACTIONS_VALUES.scan, content: 'Run Scan'},
|
53
|
-
];
|
54
|
-
|
55
45
|
const TABLE_SETTINGS = {
|
56
46
|
sortable: false,
|
57
47
|
};
|
@@ -73,7 +63,7 @@ const RESULT_TYPES = {
|
|
73
63
|
const b = cn('query-editor');
|
74
64
|
|
75
65
|
const propTypes = {
|
76
|
-
|
66
|
+
sendExecuteQuery: PropTypes.func,
|
77
67
|
path: PropTypes.string,
|
78
68
|
response: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
|
79
69
|
executeQuery: PropTypes.object,
|
@@ -81,6 +71,7 @@ const propTypes = {
|
|
81
71
|
setMonacoHotKey: PropTypes.func,
|
82
72
|
theme: PropTypes.string,
|
83
73
|
type: PropTypes.string,
|
74
|
+
initialQueryMode: PropTypes.string,
|
84
75
|
};
|
85
76
|
|
86
77
|
const initialTenantCommonInfoState = {
|
@@ -92,6 +83,7 @@ function QueryEditor(props) {
|
|
92
83
|
const [resultType, setResultType] = useState(RESULT_TYPES.EXECUTE);
|
93
84
|
|
94
85
|
const [isResultLoaded, setIsResultLoaded] = useState(false);
|
86
|
+
const [queryMode, setQueryMode] = useState(props.initialQueryMode);
|
95
87
|
|
96
88
|
const [resultVisibilityState, dispatchResultVisibilityState] = useReducer(
|
97
89
|
paneVisibilityToggleReducerCreator(DEFAULT_IS_QUERY_RESULT_COLLAPSED),
|
@@ -155,7 +147,7 @@ function QueryEditor(props) {
|
|
155
147
|
setMonacoHotKey(null);
|
156
148
|
switch (monacoHotKey) {
|
157
149
|
case MONACO_HOT_KEY_ACTIONS.sendQuery: {
|
158
|
-
return
|
150
|
+
return handleSendExecuteClick(queryMode);
|
159
151
|
}
|
160
152
|
case MONACO_HOT_KEY_ACTIONS.goPrev: {
|
161
153
|
return handlePreviousHistoryClick();
|
@@ -246,17 +238,17 @@ function QueryEditor(props) {
|
|
246
238
|
props.changeUserInput({input: newValue});
|
247
239
|
};
|
248
240
|
|
249
|
-
const
|
241
|
+
const handleSendExecuteClick = (mode) => {
|
250
242
|
const {
|
251
243
|
path,
|
252
|
-
executeQuery: {input, history
|
253
|
-
|
244
|
+
executeQuery: {input, history},
|
245
|
+
sendExecuteQuery,
|
254
246
|
saveQueryToHistory,
|
255
247
|
setShowPreview,
|
256
248
|
} = props;
|
257
249
|
|
258
250
|
setResultType(RESULT_TYPES.EXECUTE);
|
259
|
-
|
251
|
+
sendExecuteQuery({query: input, database: path, mode});
|
260
252
|
setIsResultLoaded(true);
|
261
253
|
setShowPreview(false);
|
262
254
|
|
@@ -267,15 +259,20 @@ function QueryEditor(props) {
|
|
267
259
|
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
|
268
260
|
};
|
269
261
|
|
270
|
-
const handleGetExplainQueryClick = () => {
|
262
|
+
const handleGetExplainQueryClick = (mode) => {
|
271
263
|
const {
|
272
264
|
path,
|
273
265
|
executeQuery: {input},
|
274
266
|
getExplainQuery,
|
275
267
|
setShowPreview,
|
276
268
|
} = props;
|
269
|
+
|
277
270
|
setResultType(RESULT_TYPES.EXPLAIN);
|
278
|
-
getExplainQuery({
|
271
|
+
getExplainQuery({
|
272
|
+
query: input,
|
273
|
+
database: path,
|
274
|
+
mode: mode,
|
275
|
+
});
|
279
276
|
setIsResultLoaded(true);
|
280
277
|
setShowPreview(false);
|
281
278
|
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
|
@@ -513,65 +510,42 @@ function QueryEditor(props) {
|
|
513
510
|
setSettingValue(SAVED_QUERIES_KEY, JSON.stringify(newSavedQueries));
|
514
511
|
};
|
515
512
|
|
516
|
-
const
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
const runText = _.find(RUN_ACTIONS, {value: runAction}).content;
|
521
|
-
|
522
|
-
const menuItems = RUN_ACTIONS.map((action) => {
|
523
|
-
return {
|
524
|
-
text: action.content,
|
525
|
-
action: () => {
|
526
|
-
selectRunAction(action.value);
|
527
|
-
setSettingValue(QUERY_INITIAL_RUN_ACTION_KEY, action.value);
|
528
|
-
},
|
529
|
-
};
|
530
|
-
});
|
513
|
+
const onUpdateQueryMode = (mode) => {
|
514
|
+
setQueryMode(mode);
|
515
|
+
props.setSettingValue(QUERY_INITIAL_MODE_KEY, mode);
|
516
|
+
};
|
531
517
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
{runText}
|
544
|
-
</Button>
|
545
|
-
<DropdownMenu
|
546
|
-
items={menuItems}
|
547
|
-
popupClassName={b('select-query-action-popup')}
|
548
|
-
switcher={
|
549
|
-
<Button
|
550
|
-
view="action"
|
551
|
-
pin="brick-round"
|
552
|
-
disabled={runIsDisabled}
|
553
|
-
loading={executeQuery.loading}
|
554
|
-
className={b('select-query-action')}
|
555
|
-
>
|
556
|
-
<Icon name="chevron-down" width={16} height={16} />
|
557
|
-
</Button>
|
558
|
-
}
|
559
|
-
/>
|
560
|
-
</div>
|
561
|
-
<Button
|
562
|
-
onClick={handleGetExplainQueryClick}
|
563
|
-
disabled={!executeQuery.input}
|
564
|
-
loading={explainQuery.loading}
|
565
|
-
>
|
566
|
-
Explain
|
567
|
-
</Button>
|
568
|
-
<Divider />
|
569
|
-
<SaveQuery
|
518
|
+
const renderControls = () => {
|
519
|
+
const {executeQuery, explainQuery, savedQueries, enableQueryModesForExplain} = props;
|
520
|
+
|
521
|
+
if (enableQueryModesForExplain) {
|
522
|
+
return (
|
523
|
+
<QueryEditorControls
|
524
|
+
onRunButtonClick={handleSendExecuteClick}
|
525
|
+
runIsLoading={executeQuery.loading}
|
526
|
+
onExplainButtonClick={handleGetExplainQueryClick}
|
527
|
+
explainIsLoading={explainQuery.loading}
|
528
|
+
onSaveQueryClick={onSaveQueryHandler}
|
570
529
|
savedQueries={savedQueries}
|
571
|
-
|
572
|
-
|
530
|
+
disabled={!executeQuery.input}
|
531
|
+
onUpdateQueryMode={onUpdateQueryMode}
|
532
|
+
queryMode={queryMode}
|
573
533
|
/>
|
574
|
-
|
534
|
+
);
|
535
|
+
}
|
536
|
+
|
537
|
+
return (
|
538
|
+
<OldQueryEditorControls
|
539
|
+
onRunButtonClick={handleSendExecuteClick}
|
540
|
+
runIsLoading={executeQuery.loading}
|
541
|
+
onExplainButtonClick={handleGetExplainQueryClick}
|
542
|
+
explainIsLoading={explainQuery.loading}
|
543
|
+
onSaveQueryClick={onSaveQueryHandler}
|
544
|
+
savedQueries={savedQueries}
|
545
|
+
disabled={!executeQuery.input}
|
546
|
+
onUpdateQueryMode={onUpdateQueryMode}
|
547
|
+
queryMode={queryMode}
|
548
|
+
/>
|
575
549
|
);
|
576
550
|
};
|
577
551
|
|
@@ -632,7 +606,9 @@ const mapStateToProps = (state) => {
|
|
632
606
|
return {
|
633
607
|
executeQuery: state.executeQuery,
|
634
608
|
explainQuery: state.explainQuery,
|
635
|
-
savedQueries:
|
609
|
+
savedQueries: getParsedSettingValue(state, SAVED_QUERIES_KEY),
|
610
|
+
initialQueryMode: getParsedSettingValue(state, QUERY_INITIAL_MODE_KEY),
|
611
|
+
enableQueryModesForExplain: getParsedSettingValue(state, ENABLE_QUERY_MODES_FOR_EXPLAIN),
|
636
612
|
showPreview: state.schema.showPreview,
|
637
613
|
currentSchema: state.schema.currentSchema,
|
638
614
|
monacoHotKey: state.executeQuery?.monacoHotKey,
|
@@ -640,7 +616,7 @@ const mapStateToProps = (state) => {
|
|
640
616
|
};
|
641
617
|
|
642
618
|
const mapDispatchToProps = {
|
643
|
-
|
619
|
+
sendExecuteQuery,
|
644
620
|
changeUserInput,
|
645
621
|
saveQueryToHistory,
|
646
622
|
goToPreviousQuery,
|
@@ -648,7 +624,6 @@ const mapDispatchToProps = {
|
|
648
624
|
getExplainQuery,
|
649
625
|
getExplainQueryAst,
|
650
626
|
setSettingValue,
|
651
|
-
selectRunAction,
|
652
627
|
setShowPreview,
|
653
628
|
setMonacoHotKey,
|
654
629
|
};
|
@@ -54,35 +54,6 @@
|
|
54
54
|
align-items: center;
|
55
55
|
}
|
56
56
|
|
57
|
-
&__controls {
|
58
|
-
display: flex;
|
59
|
-
flex: 0 0 40px;
|
60
|
-
align-items: flex-end;
|
61
|
-
|
62
|
-
min-height: 40px;
|
63
|
-
padding: 5px 20px;
|
64
|
-
|
65
|
-
border-top: 1px solid var(--yc-color-line-generic);
|
66
|
-
border-bottom: 1px solid var(--yc-color-line-generic);
|
67
|
-
background-color: var(--yc-color-base-background);
|
68
|
-
gap: 12px;
|
69
|
-
}
|
70
|
-
|
71
|
-
&__control-run {
|
72
|
-
display: flex;
|
73
|
-
align-items: center;
|
74
|
-
.yc-select__option-text {
|
75
|
-
display: none;
|
76
|
-
}
|
77
|
-
|
78
|
-
.yc-button__text {
|
79
|
-
display: flex;
|
80
|
-
justify-content: center;
|
81
|
-
align-items: center;
|
82
|
-
gap: 8px;
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
57
|
&__history-controls {
|
87
58
|
display: flex;
|
88
59
|
align-items: center;
|
@@ -93,8 +64,4 @@
|
|
93
64
|
|
94
65
|
color: var(--yc-color-text-secondary);
|
95
66
|
}
|
96
|
-
|
97
|
-
&__select-query-action {
|
98
|
-
margin-left: 2px;
|
99
|
-
}
|
100
67
|
}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
import {Button, DropdownMenu} from '@gravity-ui/uikit';
|
2
|
+
import {useMemo} from 'react';
|
3
|
+
|
4
|
+
import {QueryModes} from '../../../../types/store/query';
|
5
|
+
import {Icon} from '../../../../components/Icon';
|
6
|
+
|
7
|
+
import SaveQuery from '../SaveQuery/SaveQuery';
|
8
|
+
|
9
|
+
import {b, QueryEditorControlsProps, QueryModeSelectorTitles} from './shared';
|
10
|
+
|
11
|
+
import './QueryEditorControls.scss';
|
12
|
+
|
13
|
+
export const OldQueryEditorControls = ({
|
14
|
+
onRunButtonClick,
|
15
|
+
runIsLoading,
|
16
|
+
onExplainButtonClick,
|
17
|
+
explainIsLoading,
|
18
|
+
onSaveQueryClick,
|
19
|
+
savedQueries,
|
20
|
+
disabled,
|
21
|
+
onUpdateQueryMode,
|
22
|
+
queryMode,
|
23
|
+
}: QueryEditorControlsProps) => {
|
24
|
+
const runModeSelectorMenuItems = useMemo(() => {
|
25
|
+
return Object.entries(QueryModeSelectorTitles).map(([mode, title]) => {
|
26
|
+
return {
|
27
|
+
text: `Run ${title}`,
|
28
|
+
action: () => {
|
29
|
+
onUpdateQueryMode(mode as QueryModes);
|
30
|
+
},
|
31
|
+
};
|
32
|
+
});
|
33
|
+
}, [onUpdateQueryMode]);
|
34
|
+
|
35
|
+
return (
|
36
|
+
<div className={b()}>
|
37
|
+
<div className={b('left')}>
|
38
|
+
<div className={b('run')}>
|
39
|
+
<Button
|
40
|
+
onClick={() => onRunButtonClick(queryMode)}
|
41
|
+
view="action"
|
42
|
+
pin="round-brick"
|
43
|
+
disabled={disabled}
|
44
|
+
loading={runIsLoading}
|
45
|
+
>
|
46
|
+
<Icon name="startPlay" viewBox="0 0 16 16" width={16} height={16} />
|
47
|
+
{`Run ${QueryModeSelectorTitles[queryMode]}`}
|
48
|
+
</Button>
|
49
|
+
<DropdownMenu
|
50
|
+
items={runModeSelectorMenuItems}
|
51
|
+
popupClassName={b('select-query-action-popup')}
|
52
|
+
switcher={
|
53
|
+
<Button
|
54
|
+
view="action"
|
55
|
+
pin="brick-round"
|
56
|
+
disabled={disabled}
|
57
|
+
loading={runIsLoading}
|
58
|
+
className={b('select-query-action')}
|
59
|
+
>
|
60
|
+
<Icon name="chevron-down" width={16} height={16} />
|
61
|
+
</Button>
|
62
|
+
}
|
63
|
+
/>
|
64
|
+
</div>
|
65
|
+
<Button
|
66
|
+
onClick={() => {
|
67
|
+
// Without defined query mode it sends 'explain' action
|
68
|
+
onExplainButtonClick();
|
69
|
+
}}
|
70
|
+
disabled={disabled}
|
71
|
+
loading={explainIsLoading}
|
72
|
+
>
|
73
|
+
Explain
|
74
|
+
</Button>
|
75
|
+
</div>
|
76
|
+
<SaveQuery
|
77
|
+
savedQueries={savedQueries}
|
78
|
+
onSaveQuery={onSaveQueryClick}
|
79
|
+
saveButtonDisabled={disabled}
|
80
|
+
/>
|
81
|
+
</div>
|
82
|
+
);
|
83
|
+
};
|
@@ -0,0 +1,57 @@
|
|
1
|
+
.ydb-query-editor-controls {
|
2
|
+
display: flex;
|
3
|
+
flex: 0 0 40px;
|
4
|
+
justify-content: space-between;
|
5
|
+
align-items: flex-end;
|
6
|
+
|
7
|
+
min-height: 40px;
|
8
|
+
padding: 5px 20px;
|
9
|
+
|
10
|
+
border-top: 1px solid var(--yc-color-line-generic);
|
11
|
+
border-bottom: 1px solid var(--yc-color-line-generic);
|
12
|
+
background-color: var(--yc-color-base-background);
|
13
|
+
gap: 24px;
|
14
|
+
|
15
|
+
&__left {
|
16
|
+
display: flex;
|
17
|
+
gap: 12px;
|
18
|
+
}
|
19
|
+
|
20
|
+
&__run {
|
21
|
+
display: flex;
|
22
|
+
align-items: center;
|
23
|
+
.yc-select__option-text {
|
24
|
+
display: none;
|
25
|
+
}
|
26
|
+
|
27
|
+
.yc-button__text {
|
28
|
+
display: flex;
|
29
|
+
justify-content: center;
|
30
|
+
align-items: center;
|
31
|
+
gap: 8px;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
&__select-query-action {
|
36
|
+
margin-left: 2px;
|
37
|
+
}
|
38
|
+
|
39
|
+
&__mode-selector {
|
40
|
+
&__popup {
|
41
|
+
width: 120px;
|
42
|
+
}
|
43
|
+
|
44
|
+
&__button {
|
45
|
+
width: 120px;
|
46
|
+
margin-left: 2px;
|
47
|
+
}
|
48
|
+
|
49
|
+
&__button-content {
|
50
|
+
display: flex;
|
51
|
+
justify-content: space-between;
|
52
|
+
align-items: center;
|
53
|
+
|
54
|
+
width: 100px;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import {Button, DropdownMenu} from '@gravity-ui/uikit';
|
2
|
+
import {useMemo} from 'react';
|
3
|
+
|
4
|
+
import {QueryModes} from '../../../../types/store/query';
|
5
|
+
import {Icon} from '../../../../components/Icon';
|
6
|
+
|
7
|
+
import SaveQuery from '../SaveQuery/SaveQuery';
|
8
|
+
|
9
|
+
import i18n from '../i18n';
|
10
|
+
|
11
|
+
import {b, QueryEditorControlsProps, QueryModeSelectorTitles} from './shared';
|
12
|
+
|
13
|
+
import './QueryEditorControls.scss';
|
14
|
+
|
15
|
+
export const QueryEditorControls = ({
|
16
|
+
onRunButtonClick,
|
17
|
+
runIsLoading,
|
18
|
+
onExplainButtonClick,
|
19
|
+
explainIsLoading,
|
20
|
+
onSaveQueryClick,
|
21
|
+
savedQueries,
|
22
|
+
disabled,
|
23
|
+
onUpdateQueryMode,
|
24
|
+
queryMode,
|
25
|
+
}: QueryEditorControlsProps) => {
|
26
|
+
const querySelectorMenuItems = useMemo(() => {
|
27
|
+
return Object.entries(QueryModeSelectorTitles).map(([mode, title]) => {
|
28
|
+
return {
|
29
|
+
text: title,
|
30
|
+
action: () => {
|
31
|
+
onUpdateQueryMode(mode as QueryModes);
|
32
|
+
},
|
33
|
+
};
|
34
|
+
});
|
35
|
+
}, [onUpdateQueryMode]);
|
36
|
+
|
37
|
+
return (
|
38
|
+
<div className={b()}>
|
39
|
+
<div className={b('left')}>
|
40
|
+
<div className={b('run')}>
|
41
|
+
<Button
|
42
|
+
onClick={() => {
|
43
|
+
onRunButtonClick(queryMode);
|
44
|
+
}}
|
45
|
+
view="action"
|
46
|
+
disabled={disabled}
|
47
|
+
loading={runIsLoading}
|
48
|
+
>
|
49
|
+
<Icon name="startPlay" viewBox="0 0 16 16" width={16} height={16} />
|
50
|
+
{'Run'}
|
51
|
+
</Button>
|
52
|
+
</div>
|
53
|
+
<Button
|
54
|
+
onClick={() => {
|
55
|
+
onExplainButtonClick(queryMode);
|
56
|
+
}}
|
57
|
+
disabled={disabled}
|
58
|
+
loading={explainIsLoading}
|
59
|
+
>
|
60
|
+
Explain
|
61
|
+
</Button>
|
62
|
+
<DropdownMenu
|
63
|
+
items={querySelectorMenuItems}
|
64
|
+
popupClassName={b('mode-selector__popup')}
|
65
|
+
switcher={
|
66
|
+
<Button className={b('mode-selector__button')}>
|
67
|
+
<span className={b('mode-selector__button-content')}>
|
68
|
+
{`${i18n('controls.query-mode-selector_type')} ${
|
69
|
+
QueryModeSelectorTitles[queryMode]
|
70
|
+
}`}
|
71
|
+
<Icon name="chevron-down" width={16} height={16} />
|
72
|
+
</span>
|
73
|
+
</Button>
|
74
|
+
}
|
75
|
+
/>
|
76
|
+
</div>
|
77
|
+
<SaveQuery
|
78
|
+
savedQueries={savedQueries}
|
79
|
+
onSaveQuery={onSaveQueryClick}
|
80
|
+
saveButtonDisabled={disabled}
|
81
|
+
/>
|
82
|
+
</div>
|
83
|
+
);
|
84
|
+
};
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import block from 'bem-cn-lite';
|
2
|
+
|
3
|
+
import {QueryModes} from '../../../../types/store/query';
|
4
|
+
|
5
|
+
export const b = block('ydb-query-editor-controls');
|
6
|
+
|
7
|
+
export const QueryModeSelectorTitles = {
|
8
|
+
[QueryModes.script]: 'Script',
|
9
|
+
[QueryModes.scan]: 'Scan',
|
10
|
+
} as const;
|
11
|
+
|
12
|
+
export interface QueryEditorControlsProps {
|
13
|
+
onRunButtonClick: (mode?: QueryModes) => void;
|
14
|
+
runIsLoading: boolean;
|
15
|
+
onExplainButtonClick: (mode?: QueryModes) => void;
|
16
|
+
explainIsLoading: boolean;
|
17
|
+
onSaveQueryClick: (queryName: string) => void;
|
18
|
+
savedQueries: unknown;
|
19
|
+
disabled: boolean;
|
20
|
+
onUpdateQueryMode: (mode: QueryModes) => void;
|
21
|
+
queryMode: QueryModes;
|
22
|
+
enableQueryModesForExplain: boolean;
|
23
|
+
}
|