ydb-embedded-ui 4.28.0 → 4.30.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 +14 -0
- package/README.md +2 -1
- package/dist/assets/illustrations/dark/error.svg +32 -0
- package/dist/assets/illustrations/light/error.svg +32 -0
- package/dist/components/ClipboardButton/ClipboardButton.tsx +52 -0
- package/dist/components/ClipboardButton/index.ts +1 -0
- package/dist/components/EntityStatus/EntityStatus.js +9 -12
- package/dist/components/EntityStatus/EntityStatus.scss +2 -13
- package/dist/components/ErrorBoundary/ErrorBoundary.scss +40 -0
- package/dist/components/ErrorBoundary/ErrorBoundary.tsx +62 -0
- package/dist/components/ErrorBoundary/i18n/en.json +7 -0
- package/dist/components/ErrorBoundary/i18n/index.ts +11 -0
- package/dist/components/ErrorBoundary/i18n/ru.json +7 -0
- package/dist/components/Illustration/Illustration.tsx +3 -1
- package/dist/containers/App/App.js +5 -2
- package/dist/containers/App/App.scss +9 -9
- package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +24 -30
- package/dist/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx +10 -16
- package/dist/containers/Versions/NodesTreeTitle/NodesTreeTitle.scss +15 -0
- package/dist/containers/Versions/NodesTreeTitle/NodesTreeTitle.tsx +9 -6
- package/dist/index.tsx +8 -5
- package/dist/utils/registerError.ts +18 -0
- package/package.json +3 -2
- package/dist/components/CopyToClipboard/CopyToClipboard.tsx +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.30.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.29.0...v4.30.0) (2024-01-16)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add clipboard button to nodes tree titles ([#648](https://github.com/ydb-platform/ydb-embedded-ui/issues/648)) ([1411651](https://github.com/ydb-platform/ydb-embedded-ui/commit/141165173189be064e9e9314b42aa3eb7fce9c69))
|
|
9
|
+
|
|
10
|
+
## [4.29.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.28.0...v4.29.0) (2024-01-12)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add ErrorBoundary ([#549](https://github.com/ydb-platform/ydb-embedded-ui/issues/549)) ([f5ad224](https://github.com/ydb-platform/ydb-embedded-ui/commit/f5ad224b342e0fa25b1bafa3f5e2202ce165ef80))
|
|
16
|
+
|
|
3
17
|
## [4.28.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v4.27.1...v4.28.0) (2024-01-10)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Local viewer for YDB clusters
|
|
4
4
|
|
|
5
|
-
[Docs for users](https://ydb.tech/en/docs/maintenance/embedded_monitoring/ydb_monitoring)
|
|
5
|
+
* [Docs for users](https://ydb.tech/en/docs/maintenance/embedded_monitoring/ydb_monitoring)
|
|
6
|
+
* [Project Roadmap](ROADMAP.md)
|
|
6
7
|
|
|
7
8
|
## Preview
|
|
8
9
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="230" height="230" fill="none">
|
|
2
|
+
<path fill="#BECFE0" fill-opacity=".8" fill-rule="evenodd" d="M169.001 51.666c5.523 0 10 4.477 10 10v21.017l18.197-10.506c4.783-2.762 10.899-1.123 13.66 3.66 2.761 4.783 1.123 10.899-3.66 13.66l-18.197 10.507 18.198 10.506c4.783 2.762 6.421 8.878 3.66 13.661-2.762 4.782-8.877 6.421-13.66 3.66l-18.198-10.506v21.008c0 5.523-4.477 10-10 10-5.522 0-10-4.477-10-10v-21.009l-18.199 10.507c-4.782 2.761-10.898 1.122-13.66-3.66-2.761-4.783-1.122-10.899 3.66-13.661l18.199-10.506-18.198-10.507c-4.783-2.761-6.421-8.877-3.66-13.66 2.762-4.783 8.877-6.422 13.66-3.66l18.198 10.507V61.666c0-5.523 4.478-10 10-10Z" clip-rule="evenodd"/>
|
|
3
|
+
<path fill="#E7E7E7" fill-rule="evenodd" d="M171.523 95.922a11.003 11.003 0 0 1 1.099 8.347l-13.208 49.291c-1.572 5.868-7.604 9.351-13.472 7.778l-25.356-6.794a44.998 44.998 0 0 1-.53 1.929l25.368 6.797c6.935 1.858 14.064-2.257 15.922-9.192l13.207-49.291c.893-3.33.426-6.879-1.298-9.865L155.598 64.34a12.999 12.999 0 0 0-7.894-6.057l-29.972-8.031c-6.935-1.858-14.063 2.257-15.922 9.192l-11.328 42.277c.64.192 1.276.398 1.905.618l11.355-42.377c1.573-5.868 7.604-9.35 13.472-7.778l29.973 8.03a11 11 0 0 1 6.679 5.126l17.657 30.582Z" clip-rule="evenodd"/>
|
|
4
|
+
<path fill="#FF5958" fill-opacity=".8" d="M35.388 155.273c-6.29-23.472 7.64-47.599 31.113-53.889 23.472-6.289 47.599 7.641 53.889 31.113 6.289 23.473-7.641 47.599-31.113 53.889-23.473 6.289-47.6-7.64-53.889-31.113Z"/>
|
|
5
|
+
<path stroke="#E7E7E7" stroke-width="2" d="M60.636 117.734c53.586-33.459-26.868-81.505-36.557-61.318-11.802 24.59 99.395 51.098 128.865-26.3"/>
|
|
6
|
+
<mask id="b" width="89" height="89" x="33" y="99" maskUnits="userSpaceOnUse" style="mask-type:alpha">
|
|
7
|
+
<path fill="#FF5958" fill-opacity=".9" d="M35.388 155.273c-6.29-23.472 7.64-47.599 31.113-53.889 23.472-6.289 47.599 7.641 53.889 31.113 6.289 23.473-7.641 47.599-31.113 53.889-23.473 6.289-47.6-7.64-53.889-31.113Z"/>
|
|
8
|
+
</mask>
|
|
9
|
+
<g filter="url(#a)" mask="url(#b)">
|
|
10
|
+
<path stroke="#E7E7E7" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".6" stroke-width="2" d="M172.389 95.422a12.004 12.004 0 0 1 1.199 9.106l-13.208 49.291c-1.715 6.401-8.295 10.2-14.697 8.485L91.591 147.81c-6.401-1.715-10.2-8.295-8.485-14.697l19.67-73.41c1.716-6.402 8.296-10.2 14.697-8.485l29.972 8.03a11.998 11.998 0 0 1 7.287 5.592l17.657 30.582Z"/>
|
|
11
|
+
</g>
|
|
12
|
+
<g filter="url(#c)">
|
|
13
|
+
<path fill="#fff" fill-opacity=".72" fill-rule="evenodd" d="M80.866 130.432a6.359 6.359 0 1 1-12.284 3.29 6.359 6.359 0 0 1 12.284-3.29Zm4.817-1.291c1.621 6.052-1.97 12.273-8.022 13.894-6.052 1.622-12.273-1.97-13.895-8.022-1.621-6.052 1.97-12.272 8.022-13.894 6.052-1.622 12.273 1.97 13.895 8.022Zm-21.346 32.565c-.154-.577-.009-2.61 2.877-5.555 2.665-2.721 6.917-5.33 12.158-6.734 5.24-1.404 10.227-1.271 13.896-.247 3.971 1.108 5.114 2.796 5.268 3.372a3.116 3.116 0 0 1-2.204 3.817l-28.178 7.55a3.116 3.116 0 0 1-3.817-2.203ZM78.081 144.6c-12.054 3.23-20.238 12.134-18.56 18.396a8.103 8.103 0 0 0 9.924 5.73l28.178-7.55a8.104 8.104 0 0 0 5.73-9.925c-1.678-6.261-13.218-9.881-25.272-6.651Z" clip-rule="evenodd"/>
|
|
14
|
+
</g>
|
|
15
|
+
<defs>
|
|
16
|
+
<filter id="a" width="113.303" height="133.91" x="71.693" y="39.806" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
17
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
18
|
+
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
19
|
+
<feGaussianBlur result="effect1_foregroundBlur_1301_35376" stdDeviation="5"/>
|
|
20
|
+
</filter>
|
|
21
|
+
<filter id="c" width="73.289" height="73.288" x="41.018" y="106.391" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
22
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
23
|
+
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
24
|
+
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
25
|
+
<feOffset/>
|
|
26
|
+
<feGaussianBlur stdDeviation="1.917"/>
|
|
27
|
+
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
|
28
|
+
<feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.8 0"/>
|
|
29
|
+
<feBlend in2="shape" result="effect1_innerShadow_1301_35376"/>
|
|
30
|
+
</filter>
|
|
31
|
+
</defs>
|
|
32
|
+
</svg>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="230" height="230" fill="none">
|
|
2
|
+
<path fill="#BECFE0" fill-opacity=".9" fill-rule="evenodd" d="M169.001 51.666c5.523 0 10 4.477 10 10v21.017l18.197-10.506c4.783-2.762 10.899-1.123 13.66 3.66 2.761 4.783 1.123 10.899-3.66 13.66l-18.197 10.507 18.198 10.506c4.783 2.762 6.421 8.878 3.66 13.661-2.762 4.782-8.877 6.421-13.66 3.66l-18.198-10.506v21.008c0 5.523-4.477 10-10 10-5.522 0-10-4.477-10-10v-21.009l-18.199 10.507c-4.782 2.761-10.898 1.122-13.66-3.66-2.761-4.783-1.122-10.899 3.66-13.661l18.199-10.506-18.198-10.507c-4.783-2.761-6.421-8.877-3.66-13.66 2.762-4.783 8.877-6.422 13.66-3.66l18.198 10.507V61.666c0-5.523 4.478-10 10-10Z" clip-rule="evenodd"/>
|
|
3
|
+
<path fill="#262626" fill-rule="evenodd" d="M171.523 95.922a11.003 11.003 0 0 1 1.099 8.347l-13.208 49.291c-1.572 5.868-7.604 9.351-13.472 7.778l-25.356-6.794a44.998 44.998 0 0 1-.53 1.929l25.368 6.797c6.935 1.858 14.064-2.257 15.922-9.192l13.207-49.291c.893-3.33.426-6.879-1.298-9.865L155.598 64.34a12.999 12.999 0 0 0-7.894-6.057l-29.972-8.031c-6.935-1.858-14.063 2.257-15.922 9.192l-11.328 42.277c.64.192 1.276.398 1.905.618l11.355-42.377c1.573-5.868 7.604-9.35 13.472-7.778l29.973 8.03a11 11 0 0 1 6.679 5.126l17.657 30.582Z" clip-rule="evenodd"/>
|
|
4
|
+
<path fill="#FF5958" fill-opacity=".9" d="M35.388 155.273c-6.29-23.472 7.64-47.599 31.113-53.889 23.472-6.289 47.599 7.641 53.889 31.113 6.289 23.473-7.641 47.599-31.113 53.889-23.473 6.289-47.6-7.64-53.889-31.113Z"/>
|
|
5
|
+
<path stroke="#262626" stroke-width="2" d="M60.636 117.734c53.586-33.459-26.868-81.505-36.557-61.318-11.802 24.59 99.395 51.098 128.865-26.3"/>
|
|
6
|
+
<mask id="b" width="89" height="89" x="33" y="99" maskUnits="userSpaceOnUse" style="mask-type:alpha">
|
|
7
|
+
<path fill="#FF5958" fill-opacity=".9" d="M35.388 155.273c-6.29-23.472 7.64-47.599 31.113-53.889 23.472-6.289 47.599 7.641 53.889 31.113 6.289 23.473-7.641 47.599-31.113 53.889-23.473 6.289-47.6-7.64-53.889-31.113Z"/>
|
|
8
|
+
</mask>
|
|
9
|
+
<g filter="url(#a)" mask="url(#b)">
|
|
10
|
+
<path stroke="#262626" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".6" stroke-width="2" d="M172.389 95.422a12.004 12.004 0 0 1 1.199 9.106l-13.208 49.291c-1.715 6.401-8.295 10.2-14.697 8.485L91.591 147.81c-6.401-1.715-10.2-8.295-8.485-14.697l19.67-73.41c1.716-6.402 8.296-10.2 14.697-8.485l29.972 8.03a11.998 11.998 0 0 1 7.287 5.592l17.657 30.582Z"/>
|
|
11
|
+
</g>
|
|
12
|
+
<g filter="url(#c)">
|
|
13
|
+
<path fill="#fff" fill-opacity=".72" fill-rule="evenodd" d="M80.866 130.432a6.359 6.359 0 1 1-12.284 3.29 6.359 6.359 0 0 1 12.284-3.29Zm4.817-1.291c1.621 6.052-1.97 12.273-8.022 13.894-6.052 1.622-12.273-1.97-13.895-8.022-1.621-6.052 1.97-12.272 8.022-13.894 6.052-1.622 12.273 1.97 13.895 8.022Zm-21.346 32.565c-.154-.577-.009-2.61 2.877-5.555 2.665-2.721 6.917-5.33 12.158-6.734 5.24-1.404 10.227-1.271 13.896-.247 3.971 1.108 5.114 2.796 5.268 3.372a3.116 3.116 0 0 1-2.204 3.817l-28.178 7.55a3.116 3.116 0 0 1-3.817-2.203ZM78.081 144.6c-12.054 3.23-20.238 12.134-18.56 18.396a8.103 8.103 0 0 0 9.924 5.73l28.178-7.55a8.104 8.104 0 0 0 5.73-9.925c-1.678-6.261-13.218-9.881-25.272-6.651Z" clip-rule="evenodd"/>
|
|
14
|
+
</g>
|
|
15
|
+
<defs>
|
|
16
|
+
<filter id="a" width="113.303" height="133.91" x="71.693" y="39.806" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
17
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
18
|
+
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
19
|
+
<feGaussianBlur result="effect1_foregroundBlur_1301_31085" stdDeviation="5"/>
|
|
20
|
+
</filter>
|
|
21
|
+
<filter id="c" width="73.289" height="73.288" x="41.018" y="106.391" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
22
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
23
|
+
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
24
|
+
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
25
|
+
<feOffset/>
|
|
26
|
+
<feGaussianBlur stdDeviation="1.917"/>
|
|
27
|
+
<feComposite in2="hardAlpha" k2="-1" k3="1" operator="arithmetic"/>
|
|
28
|
+
<feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.8 0"/>
|
|
29
|
+
<feBlend in2="shape" result="effect1_innerShadow_1301_31085"/>
|
|
30
|
+
</filter>
|
|
31
|
+
</defs>
|
|
32
|
+
</svg>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ButtonProps,
|
|
4
|
+
ClipboardIcon,
|
|
5
|
+
CopyToClipboard as CopyToClipboardUiKit,
|
|
6
|
+
CopyToClipboardStatus,
|
|
7
|
+
Tooltip,
|
|
8
|
+
} from '@gravity-ui/uikit';
|
|
9
|
+
import cn from 'bem-cn-lite';
|
|
10
|
+
|
|
11
|
+
const b = cn('clipboard-button');
|
|
12
|
+
|
|
13
|
+
interface ClipboardButtonProps extends Pick<ButtonProps, 'disabled' | 'size' | 'title' | 'view'> {
|
|
14
|
+
className?: string;
|
|
15
|
+
text: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* An inner component required
|
|
20
|
+
* because `react-copy-to-clipboard` doesn't work with `Tooltip` otherwise.
|
|
21
|
+
*/
|
|
22
|
+
function InnerButton({
|
|
23
|
+
className,
|
|
24
|
+
status,
|
|
25
|
+
title,
|
|
26
|
+
...props
|
|
27
|
+
}: Omit<ClipboardButtonProps, 'text'> & {status: CopyToClipboardStatus}) {
|
|
28
|
+
return (
|
|
29
|
+
<Tooltip
|
|
30
|
+
content={status === CopyToClipboardStatus.Success ? 'Copied!' : title || 'Copy'}
|
|
31
|
+
/**
|
|
32
|
+
* Auto-placement has a bug with text changing.
|
|
33
|
+
* @link https://github.com/ydb-platform/ydb-embedded-ui/pull/648#discussion_r1453530092
|
|
34
|
+
*/
|
|
35
|
+
placement="bottom-start"
|
|
36
|
+
>
|
|
37
|
+
<Button {...props} className={b(null, className)}>
|
|
38
|
+
<Button.Icon>
|
|
39
|
+
<ClipboardIcon status={status} size={16} />
|
|
40
|
+
</Button.Icon>
|
|
41
|
+
</Button>
|
|
42
|
+
</Tooltip>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function ClipboardButton({text, ...props}: ClipboardButtonProps) {
|
|
47
|
+
return (
|
|
48
|
+
<CopyToClipboardUiKit text={text} timeout={1000}>
|
|
49
|
+
{(status) => <InnerButton {...props} status={status} />}
|
|
50
|
+
</CopyToClipboardUiKit>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ClipboardButton';
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
1
|
+
import {Icon, Link as UIKitLink} from '@gravity-ui/uikit';
|
|
3
2
|
import cn from 'bem-cn-lite';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import React from 'react';
|
|
4
5
|
import {Link} from 'react-router-dom';
|
|
5
|
-
import {ClipboardButton, Link as UIKitLink, Button, Icon} from '@gravity-ui/uikit';
|
|
6
|
-
|
|
7
|
-
import circleInfoIcon from '../../assets/icons/circle-info.svg';
|
|
8
6
|
import circleExclamationIcon from '../../assets/icons/circle-exclamation.svg';
|
|
9
|
-
import
|
|
7
|
+
import circleInfoIcon from '../../assets/icons/circle-info.svg';
|
|
10
8
|
import circleTimesIcon from '../../assets/icons/circle-xmark.svg';
|
|
11
|
-
|
|
9
|
+
import triangleExclamationIcon from '../../assets/icons/triangle-exclamation.svg';
|
|
10
|
+
import {ClipboardButton} from '../ClipboardButton';
|
|
12
11
|
import './EntityStatus.scss';
|
|
13
12
|
|
|
14
13
|
const icons = {
|
|
@@ -121,15 +120,13 @@ class EntityStatus extends React.Component {
|
|
|
121
120
|
{this.renderLink()}
|
|
122
121
|
</span>
|
|
123
122
|
{hasClipboardButton && (
|
|
124
|
-
<
|
|
125
|
-
|
|
123
|
+
<ClipboardButton
|
|
124
|
+
text={name}
|
|
126
125
|
size="s"
|
|
127
126
|
className={b('clipboard-button', {
|
|
128
127
|
visible: this.props.clipboardButtonAlwaysVisible,
|
|
129
128
|
})}
|
|
130
|
-
|
|
131
|
-
<ClipboardButton text={name} size={16} />
|
|
132
|
-
</Button>
|
|
129
|
+
/>
|
|
133
130
|
)}
|
|
134
131
|
</div>
|
|
135
132
|
);
|
|
@@ -10,25 +10,14 @@
|
|
|
10
10
|
@include body-2-typography();
|
|
11
11
|
|
|
12
12
|
&__clipboard-button {
|
|
13
|
-
|
|
14
|
-
justify-content: center;
|
|
15
|
-
align-items: center;
|
|
13
|
+
visibility: hidden;
|
|
16
14
|
|
|
17
15
|
margin-left: 8px;
|
|
18
16
|
|
|
19
17
|
color: var(--yc-color-text-secondary);
|
|
20
18
|
|
|
21
|
-
.yc-button__text {
|
|
22
|
-
margin: 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.yc-clipboard-button {
|
|
26
|
-
width: 24px;
|
|
27
|
-
height: 24px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
&_visible {
|
|
31
|
-
|
|
20
|
+
visibility: visible;
|
|
32
21
|
}
|
|
33
22
|
}
|
|
34
23
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
@import '../../styles/mixins.scss';
|
|
2
|
+
|
|
3
|
+
.ydb-error-boundary {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: row;
|
|
6
|
+
align-items: flex-start;
|
|
7
|
+
|
|
8
|
+
padding: 20px;
|
|
9
|
+
|
|
10
|
+
@include body-2-typography();
|
|
11
|
+
|
|
12
|
+
&__illustration {
|
|
13
|
+
width: 230px;
|
|
14
|
+
height: 230px;
|
|
15
|
+
margin-right: 20px;
|
|
16
|
+
}
|
|
17
|
+
&__error-title {
|
|
18
|
+
margin-top: 44px;
|
|
19
|
+
@include lead-typography();
|
|
20
|
+
}
|
|
21
|
+
&__error-description {
|
|
22
|
+
margin-top: 12px;
|
|
23
|
+
}
|
|
24
|
+
&__show-details {
|
|
25
|
+
margin-top: 8px;
|
|
26
|
+
}
|
|
27
|
+
&__error-details {
|
|
28
|
+
padding: 13px 18px;
|
|
29
|
+
|
|
30
|
+
border: 1px solid var(--g-color-line-generic);
|
|
31
|
+
background-color: var(--g-color-base-generic-ultralight);
|
|
32
|
+
}
|
|
33
|
+
&__actions {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: row;
|
|
36
|
+
gap: 10px;
|
|
37
|
+
|
|
38
|
+
margin-top: 20px;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {ReactNode} from 'react';
|
|
2
|
+
import {ErrorBoundary as ErrorBoundaryBase} from 'react-error-boundary';
|
|
3
|
+
import cn from 'bem-cn-lite';
|
|
4
|
+
|
|
5
|
+
import {Button, Disclosure} from '@gravity-ui/uikit';
|
|
6
|
+
|
|
7
|
+
import {registerError} from '../../utils/registerError';
|
|
8
|
+
import {Illustration} from '../Illustration';
|
|
9
|
+
import i18n from './i18n';
|
|
10
|
+
import './ErrorBoundary.scss';
|
|
11
|
+
|
|
12
|
+
const b = cn('ydb-error-boundary');
|
|
13
|
+
|
|
14
|
+
interface ErrorBoundaryProps {
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
useRetry?: boolean;
|
|
17
|
+
onReportProblem?: (error?: Error) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ErrorBoundary = ({children, useRetry = true, onReportProblem}: ErrorBoundaryProps) => {
|
|
21
|
+
return (
|
|
22
|
+
<ErrorBoundaryBase
|
|
23
|
+
onError={(error, info) => {
|
|
24
|
+
registerError(error, info.componentStack, 'error-boundary');
|
|
25
|
+
}}
|
|
26
|
+
fallbackRender={({error, resetErrorBoundary}) => {
|
|
27
|
+
return (
|
|
28
|
+
<div className={b(null)}>
|
|
29
|
+
<Illustration name="error" className={b('illustration')} />
|
|
30
|
+
<div className={b('content')}>
|
|
31
|
+
<h2 className={b('error-title')}>{i18n('error-title')}</h2>
|
|
32
|
+
<div className={b('error-description')}>
|
|
33
|
+
{i18n('error-description')}
|
|
34
|
+
</div>
|
|
35
|
+
<Disclosure
|
|
36
|
+
summary={i18n('show-details')}
|
|
37
|
+
className={b('show-details')}
|
|
38
|
+
size="m"
|
|
39
|
+
>
|
|
40
|
+
<pre className={b('error-details')}>{error.stack}</pre>
|
|
41
|
+
</Disclosure>
|
|
42
|
+
<div className={b('actions')}>
|
|
43
|
+
{useRetry && (
|
|
44
|
+
<Button view="outlined" onClick={resetErrorBoundary}>
|
|
45
|
+
{i18n('button-reset')}
|
|
46
|
+
</Button>
|
|
47
|
+
)}
|
|
48
|
+
{onReportProblem && (
|
|
49
|
+
<Button view="outlined" onClick={() => onReportProblem(error)}>
|
|
50
|
+
{i18n('report-problem')}
|
|
51
|
+
</Button>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</ErrorBoundaryBase>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {i18n, Lang} from '../../../utils/i18n';
|
|
2
|
+
|
|
3
|
+
import en from './en.json';
|
|
4
|
+
import ru from './ru.json';
|
|
5
|
+
|
|
6
|
+
const COMPONENT = 'ydb-error-boundary';
|
|
7
|
+
|
|
8
|
+
i18n.registerKeyset(Lang.En, COMPONENT, en);
|
|
9
|
+
i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
|
|
10
|
+
|
|
11
|
+
export default i18n.keyset(COMPONENT);
|
|
@@ -13,10 +13,12 @@ const store: IllustrationStore = {
|
|
|
13
13
|
light: {
|
|
14
14
|
403: () => import('../../assets/illustrations/light/403.svg'),
|
|
15
15
|
thumbsUp: () => import('../../assets/illustrations/light/thumbsUp.svg'),
|
|
16
|
+
error: () => import('../../assets/illustrations/light/error.svg'),
|
|
16
17
|
},
|
|
17
18
|
dark: {
|
|
18
19
|
403: () => import('../../assets/illustrations/dark/403.svg'),
|
|
19
|
-
thumbsUp: () => import('../../assets/illustrations/
|
|
20
|
+
thumbsUp: () => import('../../assets/illustrations/dark/thumbsUp.svg'),
|
|
21
|
+
error: () => import('../../assets/illustrations/dark/error.svg'),
|
|
20
22
|
},
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -7,6 +7,7 @@ import AsideNavigation from '../AsideNavigation/AsideNavigation';
|
|
|
7
7
|
|
|
8
8
|
import {getUser} from '../../store/reducers/authentication/authentication';
|
|
9
9
|
import {registerLanguages} from '../../utils/monaco';
|
|
10
|
+
import {ErrorBoundary} from '../../components/ErrorBoundary/ErrorBoundary';
|
|
10
11
|
|
|
11
12
|
import './App.scss';
|
|
12
13
|
|
|
@@ -38,8 +39,10 @@ class App extends React.Component {
|
|
|
38
39
|
const {singleClusterMode, clusterName} = this.props;
|
|
39
40
|
return (
|
|
40
41
|
<AsideNavigation>
|
|
41
|
-
<
|
|
42
|
-
|
|
42
|
+
<ErrorBoundary>
|
|
43
|
+
<Content singleClusterMode={singleClusterMode} clusterName={clusterName} />
|
|
44
|
+
<div id="fullscreen-root"></div>
|
|
45
|
+
</ErrorBoundary>
|
|
43
46
|
</AsideNavigation>
|
|
44
47
|
);
|
|
45
48
|
}
|
|
@@ -107,21 +107,21 @@ body,
|
|
|
107
107
|
border-right: unset;
|
|
108
108
|
border-left: unset;
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
.yc-clipboard-button {
|
|
112
|
-
display: inline-flex;
|
|
113
|
-
justify-content: center;
|
|
114
|
-
align-items: center;
|
|
115
|
-
}
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
.error {
|
|
119
113
|
color: var(--g-color-text-danger);
|
|
120
114
|
}
|
|
121
115
|
|
|
122
|
-
.data-table__row
|
|
123
|
-
.ydb-virtual-table__row
|
|
124
|
-
|
|
116
|
+
.data-table__row,
|
|
117
|
+
.ydb-virtual-table__row,
|
|
118
|
+
.ydb-tree-view__item {
|
|
119
|
+
&:hover,
|
|
120
|
+
&:focus-within {
|
|
121
|
+
& .clipboard-button {
|
|
122
|
+
visibility: visible;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
.g-root .data-table_highlight-rows .data-table__row:hover {
|
|
@@ -1,60 +1,53 @@
|
|
|
1
|
+
import {HelpPopover} from '@gravity-ui/components';
|
|
2
|
+
import {Button, Tabs} from '@gravity-ui/uikit';
|
|
3
|
+
import cn from 'bem-cn-lite';
|
|
4
|
+
import qs from 'qs';
|
|
1
5
|
import React, {ReactNode, useEffect, useReducer} from 'react';
|
|
2
6
|
import {useDispatch} from 'react-redux';
|
|
3
7
|
import {useLocation} from 'react-router';
|
|
4
8
|
import {Link} from 'react-router-dom';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import {Button, Tabs} from '@gravity-ui/uikit';
|
|
9
|
-
import {HelpPopover} from '@gravity-ui/components';
|
|
10
|
-
|
|
11
|
-
import SplitPane from '../../../components/SplitPane';
|
|
12
|
-
import CopyToClipboard from '../../../components/CopyToClipboard/CopyToClipboard';
|
|
9
|
+
import {ClipboardButton} from '../../../components/ClipboardButton';
|
|
10
|
+
import {Icon} from '../../../components/Icon';
|
|
13
11
|
import InfoViewer from '../../../components/InfoViewer/InfoViewer';
|
|
14
12
|
import {
|
|
15
13
|
CDCStreamOverview,
|
|
16
14
|
PersQueueGroupOverview,
|
|
17
15
|
} from '../../../components/InfoViewer/schemaOverview';
|
|
18
|
-
import {Icon} from '../../../components/Icon';
|
|
19
16
|
import {Loader} from '../../../components/Loader';
|
|
20
|
-
|
|
17
|
+
import SplitPane from '../../../components/SplitPane';
|
|
18
|
+
import routes, {createHref} from '../../../routes';
|
|
19
|
+
import {setShowPreview} from '../../../store/reducers/schema/schema';
|
|
20
|
+
import {
|
|
21
|
+
TENANT_PAGES_IDS,
|
|
22
|
+
TENANT_QUERY_TABS_ID,
|
|
23
|
+
TENANT_SUMMARY_TABS_IDS,
|
|
24
|
+
} from '../../../store/reducers/tenant/constants';
|
|
25
|
+
import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
|
|
21
26
|
import {
|
|
22
27
|
EPathSubType,
|
|
23
28
|
EPathType,
|
|
24
29
|
TColumnDescription,
|
|
25
30
|
TColumnTableDescription,
|
|
26
31
|
} from '../../../types/api/schema';
|
|
27
|
-
import routes, {createHref} from '../../../routes';
|
|
28
|
-
import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
|
|
29
|
-
import {useTypedSelector} from '../../../utils/hooks';
|
|
30
32
|
import {
|
|
31
33
|
DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED,
|
|
32
34
|
DEFAULT_SIZE_TENANT_SUMMARY_KEY,
|
|
33
35
|
} from '../../../utils/constants';
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
TENANT_PAGES_IDS,
|
|
38
|
-
TENANT_QUERY_TABS_ID,
|
|
39
|
-
TENANT_SUMMARY_TABS_IDS,
|
|
40
|
-
} from '../../../store/reducers/tenant/constants';
|
|
41
|
-
|
|
42
|
-
import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
|
|
43
|
-
import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
|
|
36
|
+
import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters';
|
|
37
|
+
import {useTypedSelector} from '../../../utils/hooks';
|
|
44
38
|
import {Acl} from '../Acl/Acl';
|
|
45
|
-
import
|
|
39
|
+
import i18n from '../i18n';
|
|
46
40
|
import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource';
|
|
47
|
-
|
|
41
|
+
import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable';
|
|
42
|
+
import {SchemaTree} from '../Schema/SchemaTree/SchemaTree';
|
|
43
|
+
import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer';
|
|
48
44
|
import {TenantTabsGroups, TENANT_INFO_TABS, TENANT_SCHEMA_TAB} from '../TenantPages';
|
|
49
45
|
import {
|
|
50
46
|
PaneVisibilityActionTypes,
|
|
51
|
-
paneVisibilityToggleReducerCreator,
|
|
52
47
|
PaneVisibilityToggleButtons,
|
|
48
|
+
paneVisibilityToggleReducerCreator,
|
|
53
49
|
} from '../utils/paneVisibilityToggleHelpers';
|
|
54
50
|
import {isColumnEntityType, isExternalTable, isIndexTable, isTableType} from '../utils/schema';
|
|
55
|
-
|
|
56
|
-
import i18n from '../i18n';
|
|
57
|
-
|
|
58
51
|
import './ObjectSummary.scss';
|
|
59
52
|
|
|
60
53
|
const b = cn('object-summary');
|
|
@@ -306,8 +299,9 @@ export function ObjectSummary({
|
|
|
306
299
|
</Button>
|
|
307
300
|
)}
|
|
308
301
|
{currentSchemaPath && (
|
|
309
|
-
<
|
|
302
|
+
<ClipboardButton
|
|
310
303
|
text={currentSchemaPath}
|
|
304
|
+
view="flat-secondary"
|
|
311
305
|
title={i18n('summary.copySchemaPath')}
|
|
312
306
|
/>
|
|
313
307
|
)}
|
|
@@ -1,31 +1,25 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {useDispatch} from 'react-redux';
|
|
1
|
+
import {RadioButton, Tabs} from '@gravity-ui/uikit';
|
|
3
2
|
import cn from 'bem-cn-lite';
|
|
3
|
+
import React, {useEffect, useState} from 'react';
|
|
4
4
|
import JSONTree from 'react-json-inspector';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import CopyToClipboard from '../../../../components/CopyToClipboard/CopyToClipboard';
|
|
5
|
+
import {useDispatch} from 'react-redux';
|
|
6
|
+
import {ClipboardButton} from '../../../../components/ClipboardButton';
|
|
9
7
|
import Divider from '../../../../components/Divider/Divider';
|
|
10
8
|
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
|
|
11
9
|
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
|
|
12
10
|
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
|
|
13
11
|
import {QueryResultTable} from '../../../../components/QueryResultTable/QueryResultTable';
|
|
14
|
-
|
|
12
|
+
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
|
13
|
+
import type {ColumnType, KeyValueRow} from '../../../../types/api/query';
|
|
15
14
|
import type {ValueOf} from '../../../../types/common';
|
|
16
15
|
import type {IQueryResult, QueryErrorResponse} from '../../../../types/store/query';
|
|
17
|
-
import type {ColumnType, KeyValueRow} from '../../../../types/api/query';
|
|
18
|
-
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
|
|
19
|
-
import {prepareQueryError} from '../../../../utils/query';
|
|
20
|
-
import {useTypedSelector} from '../../../../utils/hooks';
|
|
21
16
|
import {getArray} from '../../../../utils';
|
|
22
|
-
|
|
17
|
+
import {useTypedSelector} from '../../../../utils/hooks';
|
|
18
|
+
import {prepareQueryError} from '../../../../utils/query';
|
|
23
19
|
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
|
|
24
|
-
|
|
25
20
|
import {ResultIssues} from '../Issues/Issues';
|
|
26
21
|
import {QueryDuration} from '../QueryDuration/QueryDuration';
|
|
27
22
|
import {getPreparedResult} from '../utils/getPreparedResult';
|
|
28
|
-
|
|
29
23
|
import './ExecuteResult.scss';
|
|
30
24
|
|
|
31
25
|
const b = cn('ydb-query-execute-result');
|
|
@@ -115,10 +109,10 @@ export function ExecuteResult({
|
|
|
115
109
|
|
|
116
110
|
const renderClipboardButton = () => {
|
|
117
111
|
return (
|
|
118
|
-
<
|
|
112
|
+
<ClipboardButton
|
|
119
113
|
text={textResults}
|
|
114
|
+
view="flat-secondary"
|
|
120
115
|
title="Copy results"
|
|
121
|
-
toastText="Results were copied to clipboard successfully"
|
|
122
116
|
disabled={copyDisabled}
|
|
123
117
|
/>
|
|
124
118
|
);
|
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
margin-left: 30px;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
|
|
20
21
|
&__overview-container {
|
|
21
22
|
display: flex;
|
|
22
23
|
align-items: center;
|
|
23
24
|
}
|
|
25
|
+
|
|
24
26
|
&__info-label {
|
|
25
27
|
font-weight: 200;
|
|
26
28
|
|
|
@@ -52,4 +54,17 @@
|
|
|
52
54
|
width: 200px;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
57
|
+
|
|
58
|
+
&__overview-title {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&__clipboard-button {
|
|
64
|
+
visibility: hidden;
|
|
65
|
+
|
|
66
|
+
margin-left: 8px;
|
|
67
|
+
|
|
68
|
+
color: var(--yc-color-text-secondary);
|
|
69
|
+
}
|
|
55
70
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import block from 'bem-cn-lite';
|
|
2
|
-
|
|
3
1
|
import {Progress} from '@gravity-ui/uikit';
|
|
4
|
-
|
|
5
|
-
import
|
|
2
|
+
import block from 'bem-cn-lite';
|
|
3
|
+
import {ClipboardButton} from '../../../components/ClipboardButton';
|
|
6
4
|
import type {PreparedClusterNode} from '../../../store/reducers/clusterNodes/types';
|
|
5
|
+
import type {VersionValue} from '../../../types/versions';
|
|
7
6
|
import type {GroupedNodesItem} from '../types';
|
|
8
|
-
|
|
9
7
|
import './NodesTreeTitle.scss';
|
|
10
8
|
|
|
11
9
|
const b = block('ydb-versions-nodes-tree-title');
|
|
@@ -43,7 +41,12 @@ export const NodesTreeTitle = ({
|
|
|
43
41
|
{versionColor ? (
|
|
44
42
|
<div className={b('version-color')} style={{background: versionColor}} />
|
|
45
43
|
) : null}
|
|
46
|
-
|
|
44
|
+
{title ? (
|
|
45
|
+
<span className={b('overview-title')}>
|
|
46
|
+
{title}
|
|
47
|
+
<ClipboardButton text={title} size="s" className={b('clipboard-button')} />
|
|
48
|
+
</span>
|
|
49
|
+
) : null}
|
|
47
50
|
</div>
|
|
48
51
|
<div className={b('overview-info')}>
|
|
49
52
|
<div>
|
package/dist/index.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import App from './containers/App/App';
|
|
|
8
8
|
import configureStore from './store';
|
|
9
9
|
import reportWebVitals from './reportWebVitals';
|
|
10
10
|
import HistoryContext from './contexts/HistoryContext';
|
|
11
|
+
import {ErrorBoundary} from './components/ErrorBoundary/ErrorBoundary';
|
|
11
12
|
|
|
12
13
|
import './styles/themes.scss';
|
|
13
14
|
import './styles/constants.scss';
|
|
@@ -18,11 +19,13 @@ window.store = store;
|
|
|
18
19
|
|
|
19
20
|
ReactDOM.render(
|
|
20
21
|
<React.StrictMode>
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
<ErrorBoundary>
|
|
23
|
+
<Provider store={store}>
|
|
24
|
+
<HistoryContext.Provider value={history}>
|
|
25
|
+
<App />
|
|
26
|
+
</HistoryContext.Provider>
|
|
27
|
+
</Provider>
|
|
28
|
+
</ErrorBoundary>
|
|
26
29
|
</React.StrictMode>,
|
|
27
30
|
document.getElementById('root'),
|
|
28
31
|
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function registerError(error: Error, message?: string, type = 'error') {
|
|
2
|
+
if (typeof window !== 'undefined' && window.Ya?.Rum) {
|
|
3
|
+
window.Ya.Rum.logError(
|
|
4
|
+
{
|
|
5
|
+
additional: {
|
|
6
|
+
url: window.location.href,
|
|
7
|
+
},
|
|
8
|
+
type,
|
|
9
|
+
message,
|
|
10
|
+
level: window.Ya.Rum.ERROR_LEVEL.ERROR,
|
|
11
|
+
},
|
|
12
|
+
error,
|
|
13
|
+
);
|
|
14
|
+
} else {
|
|
15
|
+
// eslint-disable-next-line no-console
|
|
16
|
+
console.error(error);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ydb-embedded-ui",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.30.0",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist"
|
|
6
6
|
],
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"numeral": "2.0.6",
|
|
31
31
|
"path-to-regexp": "3.0.0",
|
|
32
32
|
"qs": "^6.11.0",
|
|
33
|
+
"react-error-boundary": "^4.0.12",
|
|
33
34
|
"react-json-inspector": "7.1.1",
|
|
34
35
|
"react-list": "0.8.11",
|
|
35
36
|
"react-monaco-editor": "0.30.1",
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
"sass": "1.32.8",
|
|
46
47
|
"url": "^0.11.0",
|
|
47
48
|
"web-vitals": "1.1.2",
|
|
48
|
-
"ydb-ui-components": "^3.
|
|
49
|
+
"ydb-ui-components": "^3.6.0"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
51
52
|
"start": "react-app-rewired start",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import {Button, CopyToClipboard as CopyToClipboardUiKit} from '@gravity-ui/uikit';
|
|
2
|
-
import createToast from '../../utils/createToast';
|
|
3
|
-
import {Icon} from '../Icon';
|
|
4
|
-
|
|
5
|
-
interface CopyToClipboardProps {
|
|
6
|
-
text: string;
|
|
7
|
-
title?: string;
|
|
8
|
-
disabled?: boolean;
|
|
9
|
-
toastText?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function CopyToClipboard(props: CopyToClipboardProps) {
|
|
13
|
-
return (
|
|
14
|
-
<CopyToClipboardUiKit text={props.text} timeout={1000}>
|
|
15
|
-
{(state) => {
|
|
16
|
-
if (state === 'success') {
|
|
17
|
-
createToast({
|
|
18
|
-
name: 'Copied',
|
|
19
|
-
title: props.toastText ?? 'Data was copied to clipboard successfully',
|
|
20
|
-
type: state,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<Button
|
|
26
|
-
disabled={props.disabled}
|
|
27
|
-
title={props.title ?? 'Copy'}
|
|
28
|
-
view="flat-secondary"
|
|
29
|
-
>
|
|
30
|
-
<Icon name="copy" viewBox={'0 0 16 16'} width={16} height={16} />
|
|
31
|
-
</Button>
|
|
32
|
-
);
|
|
33
|
-
}}
|
|
34
|
-
</CopyToClipboardUiKit>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default CopyToClipboard;
|