Skip to content

Commit

Permalink
Added simple extra popover to keep other controls out of display (#2880)
Browse files Browse the repository at this point in the history
* Added simple extra popover to keep other controls out of display

* Removed extra change

* Fixed part of tests

* Added extra comments

* Updated version & changelog
  • Loading branch information
Boris Sekachev committed Mar 1, 2021
1 parent 5bd61a4 commit 55b20e1
Show file tree
Hide file tree
Showing 21 changed files with 200 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Persistent queue added to logstash (<https://github.com/openvinotoolkit/cvat/pull/2744>)
- Improved maintanance of popups visibility (<https://github.com/openvinotoolkit/cvat/pull/2809>)
- Image visualizations settings on canvas for faster access (<https://github.com/openvinotoolkit/cvat/pull/2872>)
- Better scale management of left panel when screen is too small (<https://github.com/openvinotoolkit/cvat/pull/2880>)

### Deprecated

Expand Down
9 changes: 7 additions & 2 deletions cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 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.15.0",
"version": "1.15.1",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down Expand Up @@ -59,6 +59,7 @@
"@types/react-router-dom": "^5.1.7",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
"@types/resize-observer-browser": "^0.1.5",
"antd": "^4.12.2",
"copy-to-clipboard": "^3.3.1",
"cvat-canvas": "file:../cvat-canvas",
Expand Down
2 changes: 1 addition & 1 deletion cvat-ui/src/components/annotation-page/annotation-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
saveLogs();
const root = window.document.getElementById('root');
if (root) {
root.style.minHeight = '768px';
root.style.minHeight = '600px';
}

return () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

/// <reference types="resize-observer-browser" />

import { SmallDashOutlined } from '@ant-design/icons';
import Popover from 'antd/lib/popover';
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

const extraControlsContentClassName = 'cvat-extra-controls-control-content';

let onUpdateChildren: Function | null = null;
export function ExtraControlsControl(): JSX.Element {
const [hasChildren, setHasChildren] = useState(false);
const [initialized, setInitialized] = useState(false);

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

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

onUpdateChildren = () => {
const contentElement = window.document.getElementsByClassName(extraControlsContentClassName)[0];
if (contentElement) {
setHasChildren(contentElement.children.length > 0);
}
};

return (
<Popover
defaultVisible // we must render it at least one between using
trigger={initialized ? 'hover' : 'click'} // trigger='hover' allows to close the popover by body click
placement='right'
overlayStyle={{ display: initialized ? '' : 'none' }}
content={<div className={extraControlsContentClassName} />}
>
<SmallDashOutlined
style={{ visibility: hasChildren ? 'visible' : 'hidden' }}
className='cvat-extra-controls-control'
/>
</Popover>
);
}

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

return (props: P): JSX.Element | null => {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(true);

useEffect(() => {
if (ref && ref.current) {
const wrapper = ref.current;
const parentElement = ref.current.parentElement as HTMLElement;

const reservedHeight = 45; // for itself
const observer = new ResizeObserver(() => {
// when parent size was changed, check again can we put the control
// into the side panel or not
const availableHeight = parentElement.offsetHeight;
setVisible(availableHeight - reservedHeight >= visibilityHeightThreshold);
});

if (ref && ref.current) {
const availableHeight = parentElement.offsetHeight;
// when first mount, remember bottom coordinate which equal to minimum parent width
// to put the control into side panel
visibilityHeightThreshold = wrapper.offsetTop + wrapper.offsetHeight;
// start observing parent size
observer.observe(ref.current.parentElement as HTMLElement);
// then put it to extra controls if parent height is not enought
setVisible(availableHeight - reservedHeight >= visibilityHeightThreshold);
}

return () => {
observer.disconnect();
};
}

return () => {};
}, []);

useEffect(() => {
// when visibility changed, we notify extra content element because now its children changed
if (onUpdateChildren) {
onUpdateChildren();
}
}, [visible]);

if (!visible) {
const extraControlsContent = window.document.getElementsByClassName(extraControlsContentClassName)[0];
if (extraControlsContent) {
return ReactDOM.createPortal(<ControlComponent {...props} />, extraControlsContent);
}

return null;
}

// first mount always to side panel
return (
<div ref={ref}>
<ControlComponent {...props} />
</div>
);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ import Layout from 'antd/lib/layout';
import { ActiveControl, Rotation } from 'reducers/interfaces';
import { Canvas } from 'cvat-canvas-wrapper';

import RotateControl from './rotate-control';
import CursorControl from './cursor-control';
import MoveControl from './move-control';
import FitControl from './fit-control';
import ResizeControl from './resize-control';
import ControlVisibilityObserver, { ExtraControlsControl } from './control-visibility-observer';
import RotateControl, { Props as RotateControlProps } from './rotate-control';
import CursorControl, { Props as CursorControlProps } from './cursor-control';
import MoveControl, { Props as MoveControlProps } from './move-control';
import FitControl, { Props as FitControlProps } from './fit-control';
import ResizeControl, { Props as ResizeControlProps } from './resize-control';
import ToolsControl from './tools-control';
import OpenCVControl from './opencv-control';
import DrawRectangleControl from './draw-rectangle-control';
import DrawPolygonControl from './draw-polygon-control';
import DrawPolylineControl from './draw-polyline-control';
import DrawPointsControl from './draw-points-control';
import DrawCuboidControl from './draw-cuboid-control';
import SetupTagControl from './setup-tag-control';
import MergeControl from './merge-control';
import GroupControl from './group-control';
import SplitControl from './split-control';
import DrawRectangleControl, { Props as DrawRectangleControlProps } from './draw-rectangle-control';
import DrawPolygonControl, { Props as DrawPolygonControlProps } from './draw-polygon-control';
import DrawPolylineControl, { Props as DrawPolylineControlProps } from './draw-polyline-control';
import DrawPointsControl, { Props as DrawPointsControlProps } from './draw-points-control';
import DrawCuboidControl, { Props as DrawCuboidControlProps } from './draw-cuboid-control';
import SetupTagControl, { Props as SetupTagControlProps } from './setup-tag-control';
import MergeControl, { Props as MergeControlProps } from './merge-control';
import GroupControl, { Props as GroupControlProps } from './group-control';
import SplitControl, { Props as SplitControlProps } from './split-control';

interface Props {
canvasInstance: Canvas;
Expand All @@ -42,6 +43,25 @@ interface Props {
redrawShape(): void;
}

// We use the observer to see if these controls are in the viewport
// They automatically put to extra if not
const ObservedCursorControl = ControlVisibilityObserver<CursorControlProps>(CursorControl);
const ObservedMoveControl = ControlVisibilityObserver<MoveControlProps>(MoveControl);
const ObservedRotateControl = ControlVisibilityObserver<RotateControlProps>(RotateControl);
const ObservedFitControl = ControlVisibilityObserver<FitControlProps>(FitControl);
const ObservedResizeControl = ControlVisibilityObserver<ResizeControlProps>(ResizeControl);
const ObservedToolsControl = ControlVisibilityObserver(ToolsControl);
const ObservedOpenCVControl = ControlVisibilityObserver(OpenCVControl);
const ObservedDrawRectangleControl = ControlVisibilityObserver<DrawRectangleControlProps>(DrawRectangleControl);
const ObservedDrawPolygonControl = ControlVisibilityObserver<DrawPolygonControlProps>(DrawPolygonControl);
const ObservedDrawPolylineControl = ControlVisibilityObserver<DrawPolylineControlProps>(DrawPolylineControl);
const ObservedDrawPointsControl = ControlVisibilityObserver<DrawPointsControlProps>(DrawPointsControl);
const ObservedDrawCuboidControl = ControlVisibilityObserver<DrawCuboidControlProps>(DrawCuboidControl);
const ObservedSetupTagControl = ControlVisibilityObserver<SetupTagControlProps>(SetupTagControl);
const ObservedMergeControl = ControlVisibilityObserver<MergeControlProps>(MergeControl);
const ObservedGroupControl = ControlVisibilityObserver<GroupControlProps>(GroupControl);
const ObservedSplitControl = ControlVisibilityObserver<SplitControlProps>(SplitControl);

export default function ControlsSideBarComponent(props: Props): JSX.Element {
const {
canvasInstance,
Expand Down Expand Up @@ -170,69 +190,71 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
return (
<Layout.Sider className='cvat-canvas-controls-sidebar' theme='light' width={44}>
<GlobalHotKeys keyMap={subKeyMap} handlers={handlers} />
<CursorControl
<ObservedCursorControl
cursorShortkey={normalizedKeyMap.CANCEL}
canvasInstance={canvasInstance}
activeControl={activeControl}
/>
<MoveControl canvasInstance={canvasInstance} activeControl={activeControl} />
<RotateControl
<ObservedMoveControl canvasInstance={canvasInstance} activeControl={activeControl} />
<ObservedRotateControl
anticlockwiseShortcut={normalizedKeyMap.ANTICLOCKWISE_ROTATION}
clockwiseShortcut={normalizedKeyMap.CLOCKWISE_ROTATION}
rotateFrame={rotateFrame}
/>

<hr />

<FitControl canvasInstance={canvasInstance} />
<ResizeControl canvasInstance={canvasInstance} activeControl={activeControl} />
<ObservedFitControl canvasInstance={canvasInstance} />
<ObservedResizeControl canvasInstance={canvasInstance} activeControl={activeControl} />

<hr />
<ToolsControl />
<OpenCVControl />
<DrawRectangleControl
<ObservedToolsControl />
<ObservedOpenCVControl />
<ObservedDrawRectangleControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_RECTANGLE}
/>
<DrawPolygonControl
<ObservedDrawPolygonControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POLYGON}
/>
<DrawPolylineControl
<ObservedDrawPolylineControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POLYLINE}
/>
<DrawPointsControl
<ObservedDrawPointsControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_POINTS}
/>
<DrawCuboidControl
<ObservedDrawCuboidControl
canvasInstance={canvasInstance}
isDrawing={activeControl === ActiveControl.DRAW_CUBOID}
/>
<SetupTagControl canvasInstance={canvasInstance} isDrawing={false} />
<ObservedSetupTagControl canvasInstance={canvasInstance} isDrawing={false} />

<hr />

<MergeControl
<ObservedMergeControl
switchMergeShortcut={normalizedKeyMap.SWITCH_MERGE_MODE}
canvasInstance={canvasInstance}
activeControl={activeControl}
mergeObjects={mergeObjects}
/>
<GroupControl
<ObservedGroupControl
switchGroupShortcut={normalizedKeyMap.SWITCH_GROUP_MODE}
resetGroupShortcut={normalizedKeyMap.RESET_GROUP}
canvasInstance={canvasInstance}
activeControl={activeControl}
groupObjects={groupObjects}
/>
<SplitControl
<ObservedSplitControl
canvasInstance={canvasInstance}
switchSplitShortcut={normalizedKeyMap.SWITCH_SPLIT_MODE}
activeControl={activeControl}
splitTrack={splitTrack}
/>

<ExtraControlsControl />
</Layout.Sider>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ActiveControl } from 'reducers/interfaces';
import { Canvas } from 'cvat-canvas-wrapper';
import CVATTooltip from 'components/common/cvat-tooltip';

interface Props {
export interface Props {
canvasInstance: Canvas;
cursorShortkey: string;
activeControl: ActiveControl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { CubeIcon } from 'icons';
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
export interface Props {
canvasInstance: Canvas;
isDrawing: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces';
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
export interface Props {
canvasInstance: Canvas;
isDrawing: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces';
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
export interface Props {
canvasInstance: Canvas;
isDrawing: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces';
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
export interface Props {
canvasInstance: Canvas;
isDrawing: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ShapeType } from 'reducers/interfaces';
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
import withVisibilityHandling from './handle-popover-visibility';

interface Props {
export interface Props {
canvasInstance: Canvas;
isDrawing: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { FitIcon } from 'icons';
import { Canvas } from 'cvat-canvas-wrapper';
import CVATTooltip from 'components/common/cvat-tooltip';

interface Props {
export interface Props {
canvasInstance: Canvas;
}

Expand Down
Loading

0 comments on commit 55b20e1

Please sign in to comment.