Skip to content

Commit c1ed814

Browse files
committed
fix: link icon
1 parent 031796a commit c1ed814

File tree

6 files changed

+112
-47
lines changed

6 files changed

+112
-47
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from 'react';
2+
3+
import {Link} from '@gravity-ui/icons';
4+
import type {ButtonProps, CopyToClipboardProps, CopyToClipboardStatus} from '@gravity-ui/uikit';
5+
import {ActionTooltip, Button, CopyToClipboard, Icon} from '@gravity-ui/uikit';
6+
7+
import {cn} from '../../../../utils/cn';
8+
9+
import i18n from './i18n';
10+
11+
import './QueryDetails.scss';
12+
13+
const b = cn('kv-query-details');
14+
15+
export interface ClipboardButtonProps
16+
extends Omit<CopyToClipboardProps, 'children'>,
17+
Omit<ClipboardButtonComponentProps, 'status' | 'closeDelay' | 'onClick'> {}
18+
19+
interface ClipboardButtonComponentProps
20+
extends Omit<ButtonProps, 'href' | 'component' | 'target' | 'rel' | 'loading'> {
21+
status: CopyToClipboardStatus;
22+
closeDelay: number | undefined;
23+
/** Disable tooltip. Tooltip won't be shown */
24+
hasTooltip?: boolean;
25+
/** Text shown before copy */
26+
tooltipInitialText?: string;
27+
/** Text shown after copy */
28+
tooltipSuccessText?: string;
29+
/** Position of clipboard icon */
30+
iconPosition?: 'start' | 'end';
31+
}
32+
33+
const DEFAULT_TIMEOUT = 1200;
34+
const TOOLTIP_ANIMATION = 200;
35+
36+
const LinkButtonComponent = (props: ClipboardButtonComponentProps) => {
37+
const {size = 'm', hasTooltip = true, status, closeDelay, ...rest} = props;
38+
39+
return (
40+
<ActionTooltip
41+
title={
42+
status === 'success'
43+
? i18n('query-details.link-copied')
44+
: i18n('query-details.copy-link')
45+
}
46+
disabled={!hasTooltip}
47+
closeDelay={closeDelay}
48+
>
49+
<Button view="flat-secondary" size={size} {...rest}>
50+
<Button.Icon className={b('icon')}>
51+
<Icon data={Link} size={16} />
52+
</Button.Icon>
53+
</Button>
54+
</ActionTooltip>
55+
);
56+
};
57+
58+
export function CopyLinkButton(props: ClipboardButtonProps) {
59+
const {text, ...buttonProps} = props;
60+
61+
const timerIdRef = React.useRef<number>();
62+
const [tooltipCloseDelay, setTooltipCloseDelay] = React.useState<number | undefined>(undefined);
63+
const [tooltipDisabled, setTooltipDisabled] = React.useState(false);
64+
const timeout = DEFAULT_TIMEOUT;
65+
66+
React.useEffect(() => window.clearTimeout(timerIdRef.current), []);
67+
68+
const handleCopy = React.useCallback(() => {
69+
setTooltipDisabled(false);
70+
setTooltipCloseDelay(timeout);
71+
72+
window.clearTimeout(timerIdRef.current);
73+
74+
timerIdRef.current = window.setTimeout(() => {
75+
setTooltipDisabled(true);
76+
}, timeout - TOOLTIP_ANIMATION);
77+
}, [timeout]);
78+
79+
return (
80+
<CopyToClipboard text={text} timeout={timeout} onCopy={handleCopy}>
81+
{(status) => (
82+
<LinkButtonComponent
83+
{...buttonProps}
84+
closeDelay={tooltipCloseDelay}
85+
hasTooltip={!tooltipDisabled}
86+
status={status}
87+
/>
88+
)}
89+
</CopyToClipboard>
90+
);
91+
}

src/containers/Tenant/Diagnostics/TopQueries/QueryDetails.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,9 @@
6161
border-radius: 4px;
6262
background-color: var(--code-background-color);
6363
}
64+
65+
&__icon {
66+
// prevent button icon from firing onMouseEnter/onFocus through parent button's handler
67+
pointer-events: none;
68+
}
6469
}

src/containers/Tenant/Diagnostics/TopQueries/QueryDetails.tsx

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React from 'react';
22

3-
import {Code, Link, Xmark} from '@gravity-ui/icons';
4-
import {ActionTooltip, Button, Icon} from '@gravity-ui/uikit';
3+
import {Code, Xmark} from '@gravity-ui/icons';
4+
import {Button, Icon} from '@gravity-ui/uikit';
55

6-
import EnableFullscreenButton from '../../../../components/EnableFullscreenButton/EnableFullscreenButton';
76
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
87
import type {InfoViewerItem} from '../../../../components/InfoViewer';
98
import {InfoViewer} from '../../../../components/InfoViewer';
109
import {YDBSyntaxHighlighter} from '../../../../components/SyntaxHighlighter/YDBSyntaxHighlighter';
1110
import {cn} from '../../../../utils/cn';
1211

12+
import {CopyLinkButton} from './CopyLinkButton';
1313
import i18n from './i18n';
1414

1515
import './QueryDetails.scss';
@@ -21,55 +21,24 @@ interface QueryDetailsProps {
2121
infoItems: InfoViewerItem[];
2222
onClose: () => void;
2323
onOpenInEditor: () => void;
24-
onCopyLink?: () => void;
24+
getTopQueryUrl?: () => string;
2525
}
2626

2727
export const QueryDetails = ({
2828
queryText,
2929
infoItems,
3030
onClose,
3131
onOpenInEditor,
32-
onCopyLink,
32+
getTopQueryUrl,
3333
}: QueryDetailsProps) => {
34-
const [isTooltipOpen, setIsTooltipOpen] = React.useState(false);
35-
36-
// Function to copy current URL to clipboard
37-
const copyLinkToClipboard = (e: React.MouseEvent) => {
38-
e.stopPropagation();
39-
40-
setIsTooltipOpen(true);
41-
// If onCopyLink is provided, call it to generate and copy a shareable URL
42-
// The actual copy to clipboard is handled in the parent component
43-
if (onCopyLink) {
44-
onCopyLink();
45-
}
46-
47-
setTimeout(() => {
48-
setIsTooltipOpen(false);
49-
}, 1000);
50-
};
34+
const topQueryUrl = React.useMemo(() => getTopQueryUrl?.(), [getTopQueryUrl]);
5135

5236
return (
5337
<div className={b()}>
5438
<div className={b('header')}>
5539
<div className={b('title')}>Query</div>
5640
<div className={b('actions')}>
57-
{onCopyLink && (
58-
<ActionTooltip
59-
disabled={!isTooltipOpen}
60-
closeDelay={1000}
61-
title={i18n('query-details.link-copied')}
62-
>
63-
<Button
64-
view="flat-secondary"
65-
onClick={copyLinkToClipboard}
66-
title={i18n('query-details.copy-link')}
67-
>
68-
<Icon data={Link} size={16} />
69-
</Button>
70-
</ActionTooltip>
71-
)}
72-
<EnableFullscreenButton />
41+
{topQueryUrl && <CopyLinkButton text={topQueryUrl} />}
7342
<Button view="flat-secondary" onClick={onClose}>
7443
<Icon data={Xmark} size={16} />
7544
</Button>

src/containers/Tenant/Diagnostics/TopQueries/QueryDetailsDrawerContent.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ const b = cn('kv-top-queries');
2626
interface QueryDetailsDrawerContentProps {
2727
row: KeyValueRow | null;
2828
onClose: () => void;
29-
onCopyLink?: () => void;
29+
getTopQueryUrl?: () => string;
3030
}
3131

3232
export const QueryDetailsDrawerContent = ({
3333
row,
3434
onClose,
35-
onCopyLink,
35+
getTopQueryUrl,
3636
}: QueryDetailsDrawerContentProps) => {
3737
const dispatch = useTypedDispatch();
3838
const location = useLocation();
@@ -63,7 +63,7 @@ export const QueryDetailsDrawerContent = ({
6363
infoItems={createQueryInfoItems(row)}
6464
onClose={onClose}
6565
onOpenInEditor={handleOpenInEditor}
66-
onCopyLink={onCopyLink}
66+
getTopQueryUrl={getTopQueryUrl}
6767
/>
6868
);
6969
}

src/containers/Tenant/Diagnostics/TopQueries/RunningQueriesData.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const RunningQueriesData = ({
7777
const isDrawerVisible = selectedRow !== undefined;
7878

7979
const handleCloseDetails = React.useCallback(() => {
80-
setSelectedRow(null);
80+
setSelectedRow(undefined);
8181
}, [setSelectedRow]);
8282

8383
const renderDrawerContent = React.useCallback(() => {

src/containers/Tenant/Diagnostics/TopQueries/TopQueriesData.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ export const TopQueriesData = ({
9595

9696
const isDrawerVisible = selectedRow !== undefined;
9797

98-
const onCopyLink = React.useCallback(() => {
98+
const getTopQueryUrl = React.useCallback(() => {
9999
if (selectedRow) {
100-
const shareableUrl = generateShareableUrl(selectedRow, tableSort);
101-
navigator.clipboard.writeText(shareableUrl);
100+
return generateShareableUrl(selectedRow, tableSort);
102101
}
102+
return '';
103103
}, [selectedRow, tableSort]);
104104

105105
const renderDrawerContent = React.useCallback(() => {
@@ -110,10 +110,10 @@ export const TopQueriesData = ({
110110
<QueryDetailsDrawerContent
111111
row={selectedRow}
112112
onClose={handleCloseDetails}
113-
onCopyLink={onCopyLink}
113+
getTopQueryUrl={getTopQueryUrl}
114114
/>
115115
);
116-
}, [isDrawerVisible, selectedRow, handleCloseDetails, onCopyLink]);
116+
}, [isDrawerVisible, selectedRow, handleCloseDetails, getTopQueryUrl]);
117117

118118
const onRowClick = React.useCallback(
119119
(

0 commit comments

Comments
 (0)