diff --git a/packages/trace-viewer/src/ui/callTab.css b/packages/trace-viewer/src/ui/callTab.css index d515896d51..23e8ae0bbb 100644 --- a/packages/trace-viewer/src/ui/callTab.css +++ b/packages/trace-viewer/src/ui/callTab.css @@ -49,7 +49,7 @@ .call-line { padding: 4px 0 4px 6px; - flex: none; + display: flex; align-items: center; text-overflow: ellipsis; overflow: hidden; @@ -57,6 +57,25 @@ white-space: nowrap; } +.call-line__copy-icon { + visibility: hidden; + margin-right: 4px; +} + +.call-line:hover .call-line__copy-icon { + visibility: visible; +} + +.call-value { + text-overflow: ellipsis; + overflow: hidden; + flex: 1; +} + +.call-value::before { + content: '\00a0'; +} + .call-line .datetime, .call-line .string { color: var(--orange); diff --git a/packages/trace-viewer/src/ui/callTab.tsx b/packages/trace-viewer/src/ui/callTab.tsx index 2bc669dcfa..841d1a5da4 100644 --- a/packages/trace-viewer/src/ui/callTab.tsx +++ b/packages/trace-viewer/src/ui/callTab.tsx @@ -20,6 +20,7 @@ import type { ActionTraceEvent } from '@trace/trace'; import { msToString } from '@web/uiUtils'; import * as React from 'react'; import './callTab.css'; +import { CopyToClipboard } from './copyToClipboard'; export const CallTab: React.FunctionComponent<{ action: ActionTraceEvent | undefined, @@ -42,8 +43,8 @@ export const CallTab: React.FunctionComponent<{
{action.metadata.apiName}
{<>
Time
- {action.metadata.wallTime &&
wall time: {wallTime}
} -
duration: {duration}
+ {action.metadata.wallTime &&
wall time: {wallTime}
} +
duration: {duration}
} { !!paramKeys.length &&
Parameters
} { @@ -66,12 +67,29 @@ export const CallTab: React.FunctionComponent<{ ; }; +function shouldCopy(type: string): boolean { + return !!({ + 'string': true, + 'number': true, + 'object': true, + }[type]); +} + function renderLine(metadata: CallMetadata, name: string, value: any, key: string) { const { title, type } = toString(metadata, name, value); let text = title.replace(/\n/g, '↵'); if (type === 'string') text = `"${text}"`; - return
{name}: {text}
; + return ( +
+ {name}: {text} + { shouldCopy(type) && ( + + + + )} +
+ ); } function toString(metadata: CallMetadata, name: string, value: any): { title: string, type: string } { diff --git a/packages/trace-viewer/src/ui/copyToClipboard.css b/packages/trace-viewer/src/ui/copyToClipboard.css new file mode 100644 index 0000000000..2234a4bfa6 --- /dev/null +++ b/packages/trace-viewer/src/ui/copyToClipboard.css @@ -0,0 +1,7 @@ +.codicon-check { + color: var(--green); +} + +.codicon-close { + color: var(--red); +} diff --git a/packages/trace-viewer/src/ui/copyToClipboard.tsx b/packages/trace-viewer/src/ui/copyToClipboard.tsx new file mode 100644 index 0000000000..b8dc6d59ee --- /dev/null +++ b/packages/trace-viewer/src/ui/copyToClipboard.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as React from 'react'; +import './copyToClipboard.css'; + +const TIMEOUT = 3000; +const DEFAULT_ICON = 'codicon-clippy'; +const COPIED_ICON = 'codicon-check'; +const FAILED_ICON = 'codicon-close'; + +export const CopyToClipboard: React.FunctionComponent<{ + value: string, +}> = ({ value }) => { + const [iconClassName, setIconClassName] = React.useState(DEFAULT_ICON); + + const handleCopy = React.useCallback(() => { + navigator.clipboard.writeText(value).then(() => { + setIconClassName(COPIED_ICON); + setTimeout(() => { + setIconClassName(DEFAULT_ICON); + }, TIMEOUT); + }, () => { + setIconClassName(FAILED_ICON); + }); + + }, [value]); + + return ; +};