From 56f9a2f910b466fe8db948e89699521024756891 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Fri, 24 Jan 2025 14:34:23 +0300 Subject: [PATCH 1/2] implemented copy button --- .../src/components/annotation-page/styles.scss | 10 ++++++---- .../top-bar/player-navigation.tsx | 9 +++++++-- .../annotation-page/top-bar/top-bar.tsx | 3 +++ .../objects-side-bar/object-item.tsx | 12 ++---------- .../annotation-page/top-bar/top-bar.tsx | 17 ++++++++--------- cvat-ui/src/utils/to-clipboard.ts | 16 ++++++++++++++++ 6 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 cvat-ui/src/utils/to-clipboard.ts diff --git a/cvat-ui/src/components/annotation-page/styles.scss b/cvat-ui/src/components/annotation-page/styles.scss index 42583c1121e..daebce15aa7 100644 --- a/cvat-ui/src/components/annotation-page/styles.scss +++ b/cvat-ui/src/components/annotation-page/styles.scss @@ -186,7 +186,8 @@ .cvat-player-frame-url-icon, .cvat-player-delete-frame, -.cvat-player-restore-frame { +.cvat-player-restore-frame, +.cvat-player-copy-frame-name-icon { opacity: 0.7; color: $objects-bar-icons-color; @@ -199,9 +200,10 @@ } } -.cvat-player-delete-frame, -.cvat-player-restore-frame { - margin-left: $grid-unit-size * 2; +.cvat-player-frame-actions { + span:not(:first-child) { + margin-left: $grid-unit-size; + } } .cvat-player-frame-selector { diff --git a/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx b/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx index 8c72a2ce6e4..92bea19e3f9 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/player-navigation.tsx @@ -8,7 +8,7 @@ import React, { } from 'react'; import { Row, Col } from 'antd/lib/grid'; -import Icon, { LinkOutlined, DeleteOutlined } from '@ant-design/icons'; +import Icon, { LinkOutlined, DeleteOutlined, CopyOutlined } from '@ant-design/icons'; import Slider from 'antd/lib/slider'; import InputNumber from 'antd/lib/input-number'; import Text from 'antd/lib/typography/Text'; @@ -39,6 +39,7 @@ interface Props { onSliderChange(value: number): void; onInputChange(value: number): void; onURLIconClick(): void; + onCopyFrameFileNameIconClick(): void; onDeleteFrame(): void; onRestoreFrame(): void; switchNavigationBlocked(blocked: boolean): void; @@ -79,6 +80,7 @@ function PlayerNavigation(props: Props): JSX.Element { onSliderChange, onInputChange, onURLIconClick, + onCopyFrameFileNameIconClick, onDeleteFrame, onRestoreFrame, switchNavigationBlocked, @@ -186,7 +188,10 @@ function PlayerNavigation(props: Props): JSX.Element { {frameFilename} - + + + + diff --git a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx index a50af9299d3..73c5cac4583 100644 --- a/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx @@ -61,6 +61,7 @@ interface Props { onSliderChange(value: number): void; onInputChange(value: number): void; onURLIconClick(): void; + onCopyFrameFileNameIconClick(): void; onUndoClick(): void; onRedoClick(): void; onFinishDraw(): void; @@ -117,6 +118,7 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element { onSliderChange, onInputChange, onURLIconClick, + onCopyFrameFileNameIconClick, onUndoClick, onRedoClick, onFinishDraw, @@ -171,6 +173,7 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element { onSliderChange={onSliderChange} onInputChange={onInputChange} onURLIconClick={onURLIconClick} + onCopyFrameFileNameIconClick={onCopyFrameFileNameIconClick} onDeleteFrame={onDeleteFrame} onRestoreFrame={onRestoreFrame} switchNavigationBlocked={switchNavigationBlocked} diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index c31b9d2804b..39a8909bf53 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -31,6 +31,7 @@ import { import { Canvas, CanvasMode } from 'cvat-canvas-wrapper'; import { Canvas3d } from 'cvat-canvas3d-wrapper'; import { filterApplicableLabels } from 'utils/filter-applicable-labels'; +import { toClipboard } from 'utils/to-clipboard'; interface OwnProps { readonly: boolean; @@ -233,16 +234,7 @@ class ObjectItemContainer extends React.PureComponent { const search = `frame=${frameNumber}&type=${objectState.objectType}&serverID=${objectState.serverID}`; const url = `${origin}${pathname}?${search}`; - const fallback = (): void => { - // eslint-disable-next-line - window.prompt('Browser Clipboard API not allowed, please copy manually', url); - }; - - if (window.isSecureContext) { - window.navigator.clipboard.writeText(url).catch(fallback); - } else { - fallback(); - } + toClipboard(url); }; private switchOrientation = (): void => { diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 5c1c971beb5..387532f1b7c 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -41,6 +41,7 @@ import isAbleToChangeFrame from 'utils/is-able-to-change-frame'; import { KeyMap } from 'utils/mousetrap-react'; import { switchToolsBlockerState } from 'actions/settings-actions'; import { writeLatestFrame } from 'utils/remember-latest-frame'; +import { toClipboard } from 'utils/to-clipboard'; interface StateToProps { jobInstance: Job; @@ -567,16 +568,13 @@ class AnnotationTopBarContainer extends React.PureComponent { const { origin, pathname } = window.location; const url = `${origin}${pathname}?frame=${frameNumber}`; - const fallback = (): void => { - // eslint-disable-next-line - window.prompt('Browser Clipboard API not allowed, please copy manually', url); - }; + toClipboard(url); + }; - if (window.isSecureContext) { - window.navigator.clipboard.writeText(url).catch(fallback); - } else { - fallback(); - } + private onCopyFrameFileNameIconClick = (): void => { + const { frameFilename } = this.props; + + toClipboard(frameFilename); }; private onDeleteFrame = (): void => { @@ -670,6 +668,7 @@ class AnnotationTopBarContainer extends React.PureComponent { onSliderChange={this.onChangePlayerSliderValue} onInputChange={this.onChangePlayerInputValue} onURLIconClick={this.onURLIconClick} + onCopyFrameFileNameIconClick={this.onCopyFrameFileNameIconClick} onDeleteFrame={this.onDeleteFrame} onRestoreFrame={this.onRestoreFrame} changeWorkspace={this.changeWorkspace} diff --git a/cvat-ui/src/utils/to-clipboard.ts b/cvat-ui/src/utils/to-clipboard.ts new file mode 100644 index 00000000000..bc2a125577e --- /dev/null +++ b/cvat-ui/src/utils/to-clipboard.ts @@ -0,0 +1,16 @@ +// Copyright (C) CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +export function toClipboard(text: string): void { + const fallback = (): void => { + // eslint-disable-next-line + window.prompt('Browser Clipboard API not allowed, please copy manually', text); + }; + + if (window.isSecureContext) { + window.navigator.clipboard.writeText(text).catch(fallback); + } else { + fallback(); + } +} From 32def21ca7f9b22b8a7e134238081dafa4459c54 Mon Sep 17 00:00:00 2001 From: Kirill Lakhov Date: Fri, 24 Jan 2025 14:38:06 +0300 Subject: [PATCH 2/2] added changelog --- changelog.d/20250124_143705_klakhov_copy_frame_name_button.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/20250124_143705_klakhov_copy_frame_name_button.md diff --git a/changelog.d/20250124_143705_klakhov_copy_frame_name_button.md b/changelog.d/20250124_143705_klakhov_copy_frame_name_button.md new file mode 100644 index 00000000000..c75670c7ed1 --- /dev/null +++ b/changelog.d/20250124_143705_klakhov_copy_frame_name_button.md @@ -0,0 +1,4 @@ +### Added + +- Button to frame player to copy filename + ()