Skip to content

Commit

Permalink
Click instead of hover to open dropdown menus (#7431)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev committed Feb 9, 2024
1 parent 8bf8fe5 commit 24479c0
Show file tree
Hide file tree
Showing 65 changed files with 458 additions and 416 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Changed

- Now menus in the web interface are triggered by click, not by hover as before
(<https://github.com/opencv/cvat/pull/7431>)
2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.61.4",
"version": "1.62.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
20 changes: 12 additions & 8 deletions cvat-ui/src/components/actions-menu/actions-menu.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import './styles.scss';
import React, { useCallback } from 'react';
import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal';
import { LoadingOutlined } from '@ant-design/icons';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import { DimensionType } from 'cvat-core-wrapper';
import { DimensionType, CVATCore } from 'cvat-core-wrapper';
import Menu, { MenuInfo } from 'components/dropdown-menu';
import { usePlugins } from 'utils/hooks';
import { CombinedState } from 'reducers';

type AnnotationFormats = Awaited<ReturnType<CVATCore['server']['formats']>>;

interface Props {
taskID: number;
projectID: number | null;
taskMode: string;
bugTracker: string;
loaders: any[];
dumpers: any[];
loaders: AnnotationFormats['loaders'];
dumpers: AnnotationFormats['dumpers'];
inferenceIsActive: boolean;
taskDimension: DimensionType;
backupIsActive: boolean;
Expand Down Expand Up @@ -137,7 +137,11 @@ function ActionsMenuComponent(props: Props): JSX.Element {
);

return (
<Menu selectable={false} className='cvat-actions-menu' onClick={onClickMenuWrapper}>
<Menu
selectable={false}
className='cvat-actions-menu'
onClick={onClickMenuWrapper}
>
{ menuItems.sort((menuItem1, menuItem2) => menuItem1[1] - menuItem2[1])
.map((menuItem) => menuItem[0]) }
</Menu>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -1118,7 +1118,12 @@ class CanvasWrapperComponent extends React.PureComponent<Props> {

<BrushTools />

<Dropdown trigger={['click']} placement='topCenter' overlay={<ImageSetupsContent />}>
<Dropdown
destroyPopupOnHide
trigger={['click']}
placement='topCenter'
overlay={<ImageSetupsContent />}
>
<UpOutlined className='cvat-canvas-image-setups-trigger' />
</Dropdown>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -9,7 +9,6 @@ import { SmallDashOutlined } from '@ant-design/icons';
import Popover from 'antd/lib/popover';
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { ConnectedComponent } from 'react-redux';
import withVisibilityHandling from './handle-popover-visibility';

const extraControlsContentClassName = 'cvat-extra-controls-control-content';
Expand All @@ -19,13 +18,14 @@ const CustomPopover = withVisibilityHandling(Popover, 'extra-controls');
export function ExtraControlsControl(): JSX.Element {
const [hasChildren, setHasChildren] = useState(false);
const [initialized, setInitialized] = useState(false);
const [visible, setVisible] = useState(true);

useEffect(() => {
if (!initialized) {
setInitialized(true);
}

window.document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
setVisible(false);
}, []);

onUpdateChildren = () => {
Expand All @@ -37,7 +37,8 @@ export function ExtraControlsControl(): JSX.Element {

return (
<CustomPopover
defaultVisible // we must render it at least one between using
visible={visible}
onVisibleChange={setVisible}
trigger={initialized ? 'hover' : 'click'} // trigger='hover' allows to close the popover by body click
placement='right'
overlayStyle={{ display: initialized ? '' : 'none' }}
Expand All @@ -52,7 +53,7 @@ export function ExtraControlsControl(): JSX.Element {
}

export default function ControlVisibilityObserver<P = {}>(
ControlComponent: React.FunctionComponent<P> | ConnectedComponent<any, any>,
ControlComponent: React.FunctionComponent<P>,
): React.FunctionComponent<P> {
let visibilityHeightThreshold = 0; // minimum value of height when element can be pushed to main panel

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React from 'react';
import Layout from 'antd/lib/layout';

import {
ActiveControl, ObjectType, Rotation, ShapeType, CombinedState,
ActiveControl, Rotation, CombinedState,
} from 'reducers';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Canvas, CanvasMode } from 'cvat-canvas-wrapper';
import { LabelType } from 'cvat-core-wrapper';

import ControlVisibilityObserver, { ExtraControlsControl } from './control-visibility-observer';
import RotateControl, { Props as RotateControlProps } from './rotate-control';
Expand Down Expand Up @@ -105,14 +106,14 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
let tagControlVisible = withUnspecifiedType;
const skeletonControlVisible = labels.some((label: Label) => label.type === 'skeleton');
labels.forEach((label: Label) => {
rectangleControlVisible = rectangleControlVisible || label.type === ShapeType.RECTANGLE;
polygonControlVisible = polygonControlVisible || label.type === ShapeType.POLYGON;
polylineControlVisible = polylineControlVisible || label.type === ShapeType.POLYLINE;
pointsControlVisible = pointsControlVisible || label.type === ShapeType.POINTS;
ellipseControlVisible = ellipseControlVisible || label.type === ShapeType.ELLIPSE;
cuboidControlVisible = cuboidControlVisible || label.type === ShapeType.CUBOID;
maskControlVisible = maskControlVisible || label.type === ShapeType.MASK;
tagControlVisible = tagControlVisible || label.type === ObjectType.TAG;
rectangleControlVisible = rectangleControlVisible || label.type === LabelType.RECTANGLE;
polygonControlVisible = polygonControlVisible || label.type === LabelType.POLYGON;
polylineControlVisible = polylineControlVisible || label.type === LabelType.POLYLINE;
pointsControlVisible = pointsControlVisible || label.type === LabelType.POINTS;
ellipseControlVisible = ellipseControlVisible || label.type === LabelType.ELLIPSE;
cuboidControlVisible = cuboidControlVisible || label.type === LabelType.CUBOID;
maskControlVisible = maskControlVisible || label.type === LabelType.MASK;
tagControlVisible = tagControlVisible || label.type === LabelType.TAG;
});

const preventDefault = (event: KeyboardEvent | undefined): void => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -150,13 +150,11 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
</Row>
) : null}
<Row justify='space-around'>
<Col span={12}>
<Col span={24}>
<CVATTooltip title={`Press ${repeatShapeShortcut} to draw again`}>
<Button className={`cvat-draw-${shapeType}-shape-button`} onClick={onDrawShape}>Shape</Button>
</CVATTooltip>
</Col>
{shapeType !== ShapeType.MASK && (
<Col span={12}>
{shapeType !== ShapeType.MASK && (
<CVATTooltip title={`Press ${repeatShapeShortcut} to draw again`}>
<Button
className={`cvat-draw-${shapeType}-track-button`}
Expand All @@ -165,8 +163,8 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
Track
</Button>
</CVATTooltip>
</Col>
)}
)}
</Col>
</Row>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import React, { useState } from 'react';
import React from 'react';
import Popover, { PopoverProps } from 'antd/lib/popover';

interface OwnProps {
Expand All @@ -12,7 +13,6 @@ interface OwnProps {

export default function withVisibilityHandling(WrappedComponent: typeof Popover, popoverType: string) {
return function (props: OwnProps & PopoverProps): JSX.Element {
const [visible, setVisible] = useState<boolean>(false);
const { overlayClassName, onVisibleChange, ...rest } = props;
const overlayClassNames = typeof overlayClassName === 'string' ? overlayClassName.split(/\s+/) : [];
const popoverClassName = `cvat-${popoverType}-popover`;
Expand All @@ -27,7 +27,7 @@ export default function withVisibilityHandling(WrappedComponent: typeof Popover,
animationDuration: '0s',
animationDelay: '0s',
}}
trigger={visible ? ['click'] : ['click', 'hover']}
trigger={['click']}
overlayClassName={overlayClassNames.join(' ').trim()}
onVisibleChange={(_visible: boolean) => {
if (_visible) {
Expand All @@ -38,7 +38,6 @@ export default function withVisibilityHandling(WrappedComponent: typeof Popover,
(element as HTMLElement).style.opacity = '';
}
}
setVisible(_visible);
if (onVisibleChange) onVisibleChange(_visible);
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -42,7 +43,6 @@ function RotateControl(props: Props): JSX.Element {
</CVATTooltip>
</>
)}
trigger='hover'
>
<Icon className='cvat-rotate-canvas-control' component={RotateIcon} />
</CustomPopover>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -1100,6 +1100,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
</Col>
<Col span={2} className='cvat-interactors-tips-icon-container'>
<Dropdown
destroyPopupOnHide
overlay={(
<ToolsTooltips
name={activeInteractor?.name}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2020-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand All @@ -13,6 +13,7 @@ import { ObjectType, ShapeType, ColorBy } from 'reducers';
import CVATTooltip from 'components/common/cvat-tooltip';
import LabelSelector from 'components/label-selector/label-selector';
import ItemMenu from './object-item-menu';
import ColorPicker from './color-picker';

interface Props {
jobInstance: any;
Expand Down Expand Up @@ -87,21 +88,8 @@ function ItemTopComponent(props: Props): JSX.Element {
jobInstance,
} = props;

const [menuVisible, setMenuVisible] = useState(false);
const [colorPickerVisible, setColorPickerVisible] = useState(false);

const changeMenuVisible = (visible: boolean): void => {
if (!visible && colorPickerVisible) return;
setMenuVisible(visible);
};

const changeColorPickerVisible = (visible: boolean): void => {
if (!visible) {
setMenuVisible(false);
}
setColorPickerVisible(visible);
};

return (
<Row align='middle'>
<Col span={10}>
Expand Down Expand Up @@ -129,12 +117,24 @@ function ItemTopComponent(props: Props): JSX.Element {
</CVATTooltip>
</Col>
{ !isGroundTruth && (
<Col span={2}>
colorPickerVisible ? (
<ColorPicker
visible
value={color}
onVisibleChange={setColorPickerVisible}
onChange={(_color: string) => {
changeColor(_color);
}}
>
<Col span={2}>
<MoreOutlined />
</Col>
</ColorPicker>
) : (
<Dropdown
visible={menuVisible}
onVisibleChange={changeMenuVisible}
destroyPopupOnHide
placement='bottomLeft'
trigger={menuVisible ? ['click'] : ['click', 'hover']}
trigger={['click']}
overlay={ItemMenu({
jobInstance,
readonly,
Expand Down Expand Up @@ -162,14 +162,16 @@ function ItemTopComponent(props: Props): JSX.Element {
toBackground,
toForeground,
resetCuboidPerspective,
changeColorPickerVisible,
setColorPickerVisible,
edit,
slice,
})}
>
<MoreOutlined />
<Col span={2}>
<MoreOutlined />
</Col>
</Dropdown>
</Col>
)
)}
</Row>
);
Expand Down
Loading

0 comments on commit 24479c0

Please sign in to comment.