React UI: batch of fixes (#1525)

main
Dmitry Kalinin 6 years ago committed by GitHub
parent 3caae98b5b
commit 2e8117561c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,6 +33,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Exporting frame stepped task (https://github.com/opencv/cvat/issues/1294, https://github.com/opencv/cvat/issues/1334) - Exporting frame stepped task (https://github.com/opencv/cvat/issues/1294, https://github.com/opencv/cvat/issues/1334)
- Fixed broken command line interface for `cvat` export format in Datumaro (https://github.com/opencv/cvat/issues/1494) - Fixed broken command line interface for `cvat` export format in Datumaro (https://github.com/opencv/cvat/issues/1494)
- Updated Rest API document, Swagger document serving instruction issue (https://github.com/opencv/cvat/issues/1495) - Updated Rest API document, Swagger document serving instruction issue (https://github.com/opencv/cvat/issues/1495)
- Fixed cuboid occluded view (<https://github.com/opencv/cvat/pull/1500>)
- Non-informative lock icon (<https://github.com/opencv/cvat/pull/1434>)
- Sidebar in AAM has no hide/show button (<https://github.com/opencv/cvat/pull/1420>)
- Task/Job buttons has no "Open in new tab" option (<https://github.com/opencv/cvat/pull/1419>)
- Delete point context menu option has no shortcut hint (<https://github.com/opencv/cvat/pull/1416>)
### Security ### Security
- -

@ -103,6 +103,10 @@ polyline.cvat_canvas_shape_splitting {
stroke-dasharray: 5; stroke-dasharray: 5;
} }
.cvat_canvas_shape .svg_select_points, .cvat_canvas_shape .cvat_canvas_cuboid_projections {
stroke-dasharray: none;
}
.cvat_canvas_autoborder_point { .cvat_canvas_autoborder_point {
opacity: 0.55; opacity: 0.55;
} }

@ -427,6 +427,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
private selectize(value: boolean, shape: SVG.Element): void { private selectize(value: boolean, shape: SVG.Element): void {
const self = this; const self = this;
const { offset } = this.controller.geometry;
const translate = (points: number[]): number[] => points
.map((coord: number): number => coord - offset);
function dblClickHandler(e: MouseEvent): void { function dblClickHandler(e: MouseEvent): void {
const pointID = Array.prototype.indexOf const pointID = Array.prototype.indexOf
@ -437,10 +440,22 @@ export class CanvasViewImpl implements CanvasView, Listener {
.filter((_state: any): boolean => ( .filter((_state: any): boolean => (
_state.clientID === self.activeElement.clientID _state.clientID === self.activeElement.clientID
)); ));
if (['cuboid', 'rectangle'].includes(state.shapeType)) { if (state.shapeType === 'rectangle') {
e.preventDefault(); e.preventDefault();
return; return;
} }
if (state.shapeType === 'cuboid') {
if (e.shiftKey) {
const points = translate(pointsToArray((e.target as any)
.parentElement.parentElement.instance.attr('points')));
self.onEditDone(
state,
points,
)
e.preventDefault();
return;
}
}
if (e.ctrlKey) { if (e.ctrlKey) {
const { points } = state; const { points } = state;
self.onEditDone( self.onEditDone(

@ -263,10 +263,10 @@ function getTopDown(edgeIndex: EdgeIndex): number[] {
this.rbProj = this.line(this.updateProjectionLine(this.cuboidModel.rb.getEquation(), this.rbProj = this.line(this.updateProjectionLine(this.cuboidModel.rb.getEquation(),
this.cuboidModel.rb.points[1], this.cuboidModel.vpr)); this.cuboidModel.rb.points[1], this.cuboidModel.vpr));
this.ftProj.stroke({ color: '#C0C0C0' }); this.ftProj.stroke({ color: '#C0C0C0' }).addClass('cvat_canvas_cuboid_projections');
this.fbProj.stroke({ color: '#C0C0C0' }); this.fbProj.stroke({ color: '#C0C0C0' }).addClass('cvat_canvas_cuboid_projections');
this.rtProj.stroke({ color: '#C0C0C0' }); this.rtProj.stroke({ color: '#C0C0C0' }).addClass('cvat_canvas_cuboid_projections');
this.rbProj.stroke({ color: '#C0C0C0' }); this.rbProj.stroke({ color: '#C0C0C0' }).addClass('cvat_canvas_cuboid_projections');
}, },
setupEdges() { setupEdges() {
@ -281,7 +281,7 @@ function getTopDown(edgeIndex: EdgeIndex): number[] {
this.rightBotEdge = this.line(this.cuboidModel.rb.points); this.rightBotEdge = this.line(this.cuboidModel.rb.points);
}, },
setupGrabPoints(circleType) { setupGrabPoints(circleType: Function | string) {
const viewModel = this.cuboidModel; const viewModel = this.cuboidModel;
const circle = typeof circleType === 'function' ? circleType : this.circle; const circle = typeof circleType === 'function' ? circleType : this.circle;
@ -372,7 +372,7 @@ function getTopDown(edgeIndex: EdgeIndex): number[] {
} }
if (value === false) { if (value === false) {
this.getGrabPoints().forEach((point) => {point && point.remove()}); this.getGrabPoints().forEach((point: SVG.Element) => {point && point.remove()});
} else { } else {
this.setupGrabPoints(this.face.remember('_selectHandler').drawPoint.bind( this.setupGrabPoints(this.face.remember('_selectHandler').drawPoint.bind(
{nested: this, options: this.face.remember('_selectHandler').options} {nested: this, options: this.face.remember('_selectHandler').options}
@ -380,21 +380,29 @@ function getTopDown(edgeIndex: EdgeIndex): number[] {
// setup proper classes for selection points for proper cursor // setup proper classes for selection points for proper cursor
Array.from(this.face.remember('_selectHandler').nested.node.children) Array.from(this.face.remember('_selectHandler').nested.node.children)
.forEach((point: SVG.Circle, i: number) => { .forEach((point: SVG.LinkedHTMLElement, i: number) => {
point.classList.add(`svg_select_points_${['lt', 'lb', 'rb', 'rt'][i]}`) point.classList.add(`svg_select_points_${['lt', 'lb', 'rb', 'rt'][i]}`)
}); });
if (this.cuboidModel.orientation === Orientation.LEFT) { if (this.cuboidModel.orientation === Orientation.LEFT) {
Array.from(this.dorsalRightEdge.remember('_selectHandler').nested.node.children) Array.from(this.dorsalRightEdge.remember('_selectHandler').nested.node.children)
.forEach((point: SVG.Circle, i: number) => { .forEach((point: SVG.LinkedHTMLElement, i: number) => {
point.classList.add(`svg_select_points_${['t', 'b'][i]}`); point.classList.add(`svg_select_points_${['t', 'b'][i]}`);
point.ondblclick = this.resetPerspective.bind(this); point.ondblclick = (e: MouseEvent) => {
if (e.shiftKey) {
this.resetPerspective()
}
};
}); });
} else { } else {
Array.from(this.dorsalLeftEdge.remember('_selectHandler').nested.node.children) Array.from(this.dorsalLeftEdge.remember('_selectHandler').nested.node.children)
.forEach((point: SVG.Circle, i: number) => { .forEach((point: SVG.LinkedHTMLElement, i: number) => {
point.classList.add(`svg_select_points_${['t', 'b'][i]}`); point.classList.add(`svg_select_points_${['t', 'b'][i]}`);
point.ondblclick = this.resetPerspective.bind(this); point.ondblclick = (e: MouseEvent) => {
if (e.shiftKey) {
this.resetPerspective()
}
};
}); });
} }
@ -584,7 +592,7 @@ function getTopDown(edgeIndex: EdgeIndex): number[] {
setupDorsalEdge.call(this, this.dorsalLeftEdge, this.cuboidModel.orientation); setupDorsalEdge.call(this, this.dorsalLeftEdge, this.cuboidModel.orientation);
} }
function horizontalEdgeControl(updatingFace, midX, midY) { function horizontalEdgeControl(updatingFace: any, midX: number, midY: number) {
const leftPoints = this.updatedEdge( const leftPoints = this.updatedEdge(
this.cuboidModel.fl.points[0], this.cuboidModel.fl.points[0],
{x: midX, y: midY}, {x: midX, y: midY},

@ -1,6 +1,6 @@
{ {
"name": "cvat-ui", "name": "cvat-ui",
"version": "1.0.1", "version": "1.0.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

@ -1,6 +1,6 @@
{ {
"name": "cvat-ui", "name": "cvat-ui",
"version": "1.0.1", "version": "1.0.2",
"description": "CVAT single-page application", "description": "CVAT single-page application",
"main": "src/index.tsx", "main": "src/index.tsx",
"scripts": { "scripts": {

@ -12,6 +12,7 @@ import { SelectValue } from 'antd/lib/select';
import { CheckboxChangeEvent } from 'antd/lib/checkbox'; import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Icon from 'antd/lib/icon';
import { LogType } from 'cvat-logger'; import { LogType } from 'cvat-logger';
import { import {
@ -106,6 +107,8 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.
}, {}), }, {}),
); );
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [activeObjectState] = activatedStateID === null const [activeObjectState] = activatedStateID === null
? [null] : states.filter((objectState: any): boolean => ( ? [null] : states.filter((objectState: any): boolean => (
objectState.clientID === activatedStateID objectState.clientID === activatedStateID
@ -175,6 +178,7 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.
reverseArrow: true, reverseArrow: true,
collapsible: true, collapsible: true,
trigger: null, trigger: null,
collapsed: sidebarCollapsed,
}; };
const subKeyMap = { const subKeyMap = {
@ -218,8 +222,18 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX.
if (activeObjectState) { if (activeObjectState) {
return ( return (
<Layout.Sider {...siderProps}> <Layout.Sider {...siderProps}>
{/* eslint-disable-next-line */}
<span
className={`cvat-objects-sidebar-sider
ant-layout-sider-zero-width-trigger
ant-layout-sider-zero-width-trigger-left`}
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
>
{sidebarCollapsed ? <Icon type='menu-fold' title='Show' />
: <Icon type='menu-unfold' title='Hide' />}
</span>
<GlobalHotKeys keyMap={subKeyMap} handlers={handlers} allowChanges /> <GlobalHotKeys keyMap={subKeyMap} handlers={handlers} allowChanges />
<Row> <Row className='cvat-objects-sidebar-filter-input'>
<Col> <Col>
<AnnotationsFiltersInput /> <AnnotationsFiltersInput />
</Col> </Col>

@ -5,6 +5,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Tooltip from 'antd/lib/tooltip';
interface Props { interface Props {
activatedStateID: number | null; activatedStateID: number | null;
@ -29,9 +30,11 @@ export default function CanvasPointContextMenu(props: Props): JSX.Element | null
return ReactDOM.createPortal( return ReactDOM.createPortal(
<div className='cvat-canvas-point-context-menu' style={{ top, left }}> <div className='cvat-canvas-point-context-menu' style={{ top, left }}>
<Button type='link' icon='delete' onClick={onPointDelete}> <Tooltip title='Delete point [Ctrl + dblclick]'>
Delete point <Button type='link' icon='delete' onClick={onPointDelete}>
</Button> Delete point
</Button>
</Tooltip>
</div>, </div>,
window.document.body, window.document.body,
); );

@ -338,7 +338,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col> <Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}> <Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked { locked
? <Icon type='lock' onClick={unlock} /> ? <Icon type='lock' onClick={unlock} theme='filled'/>
: <Icon type='unlock' onClick={lock} />} : <Icon type='unlock' onClick={lock} />}
</Tooltip> </Tooltip>
</Col> </Col>
@ -388,7 +388,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col> <Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}> <Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked { locked
? <Icon type='lock' onClick={unlock} /> ? <Icon type='lock' onClick={unlock} theme='filled' />
: <Icon type='unlock' onClick={lock} />} : <Icon type='unlock' onClick={lock} />}
</Tooltip> </Tooltip>
</Col> </Col>
@ -405,7 +405,7 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element {
<Col> <Col>
<Tooltip title={`Switch lock property ${switchLockShortcut}`}> <Tooltip title={`Switch lock property ${switchLockShortcut}`}>
{ locked { locked
? <Icon type='lock' onClick={unlock} /> ? <Icon type='lock' onClick={unlock} theme='filled'/>
: <Icon type='unlock' onClick={lock} />} : <Icon type='unlock' onClick={lock} />}
</Tooltip> </Tooltip>
</Col> </Col>

@ -97,7 +97,7 @@ function ObjectListHeader(props: Props): JSX.Element {
<Col span={2}> <Col span={2}>
<Tooltip title={`Switch lock property for all ${switchLockAllShortcut}`}> <Tooltip title={`Switch lock property for all ${switchLockAllShortcut}`}>
{ statesLocked { statesLocked
? <Icon type='lock' onClick={unlockAllStates} /> ? <Icon type='lock' onClick={unlockAllStates} theme='filled' />
: <Icon type='unlock' onClick={lockAllStates} />} : <Icon type='unlock' onClick={lockAllStates} />}
</Tooltip> </Tooltip>
</Col> </Col>

@ -12,12 +12,18 @@
background-color: $background-color-1; background-color: $background-color-1;
} }
.cvat-objects-sidebar-filter-input {
width: calc(100% - 35px);
}
.cvat-objects-sidebar-sider { .cvat-objects-sidebar-sider {
top: 0px; top: 0px;
right: 0px; right: 0px;
left: auto; left: auto;
background-color: $background-color-2; background-color: $background-color-2;
border-left: 1px solid $border-color-1; border-left: 1px solid $border-color-1;
border-bottom: 1px solid $border-color-1;
border-radius: 4px 0 0 4px;
z-index: 2; z-index: 2;
} }

@ -19,6 +19,7 @@ interface Props {
dumpActivities: string[] | null; dumpActivities: string[] | null;
exportActivities: string[] | null; exportActivities: string[] | null;
installedReID: boolean; installedReID: boolean;
taskID: number;
onClickMenu(params: ClickParam, file?: File): void; onClickMenu(params: ClickParam, file?: File): void;
} }
@ -40,6 +41,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
dumpActivities, dumpActivities,
exportActivities, exportActivities,
installedReID, installedReID,
taskID,
} = props; } = props;
let latestParams: ClickParam | null = null; let latestParams: ClickParam | null = null;
@ -119,7 +121,9 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
Remove annotations Remove annotations
</Menu.Item> </Menu.Item>
<Menu.Item key={Actions.OPEN_TASK}> <Menu.Item key={Actions.OPEN_TASK}>
Open the task <a href={`/tasks/${taskID}`} onClick={(e: React.MouseEvent) => e.preventDefault()}>
Open the task
</a>
</Menu.Item> </Menu.Item>
{ installedReID && <ReIDPlugin /> } { installedReID && <ReIDPlugin /> }
</Menu> </Menu>

@ -46,9 +46,11 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element {
<div> <div>
<Button <Button
type='link' type='link'
onClick={(): void => { onClick={(e: React.MouseEvent): void => {
e.preventDefault();
push(`/tasks/${taskId}/jobs/${id}`); push(`/tasks/${taskId}/jobs/${id}`);
}} }}
href={`/tasks/${taskId}/jobs/${id}`}
> >
{`Job #${id}`} {`Job #${id}`}
</Button> </Button>

@ -184,7 +184,11 @@ class TaskItemComponent extends React.PureComponent<TaskItemProps & RouteCompone
type='primary' type='primary'
size='large' size='large'
ghost ghost
onClick={(): void => history.push(`/tasks/${id}`)} href={`/tasks/${id}`}
onClick={(e: React.MouseEvent): void => {
e.preventDefault();
history.push(`/tasks/${id}`)
}}
> >
Open Open
</Button> </Button>

@ -157,6 +157,7 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
exportActivities={exportActivities} exportActivities={exportActivities}
installedReID={installedReID} installedReID={installedReID}
onClickMenu={onClickMenu} onClickMenu={onClickMenu}
taskID={jobInstance.task.id}
/> />
); );
} }

Loading…
Cancel
Save