提交 f57586a0 编写于 作者: B Boris Sekachev 提交者: Nikita Manovich

UI Enhancements (#985)

* Single import of basic styles
* A little bit redesigned header
* Specified min resolution 1280x768
* Getting a job instance
* Improved handling when task doesn't exist
上级 21641670
......@@ -15,27 +15,3 @@ $danger-icon-color: #FF4136;
$info-icon-color: #0074D9;
$monospaced-fonts-stack: Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
hr {
border: none;
border-top: 1px solid $border-color-1;
height: 1px;
}
.cvat-text-color {
color: $text-color;
}
.cvat-title {
font-weight: 400;
font-size: 21px;
color: $text-color;
padding-top: 5px;
}
#root {
width: 100%;
height: 100%;
min-height: 100%;
display: grid;
}
......@@ -3,12 +3,45 @@ import React from 'react';
import {
Layout,
Spin,
Result,
} from 'antd';
import AnnotationTopBarComponent from './top-bar/top-bar';
import StandardWorkspaceComponent from './standard-workspace/standard-workspace';
export default function AnnotationPageComponent(): JSX.Element {
interface Props {
jobInstance: any | null | undefined;
fetching: boolean;
getJob(): void;
}
export default function AnnotationPageComponent(props: Props): JSX.Element {
const {
jobInstance,
fetching,
getJob,
} = props;
if (jobInstance === null) {
if (!fetching) {
getJob();
}
return <Spin size='large' className='cvat-spinner' />;
}
if (typeof (jobInstance) === 'undefined') {
return (
<Result
className='cvat-not-found'
status='404'
title='Sorry, but this job was not found'
subTitle='Please, be sure information you tried to get exist and you have access'
/>
);
}
return (
<Layout className='cvat-annotation-page'>
<AnnotationTopBarComponent />
......
......@@ -14,46 +14,48 @@
.cvat-annotation-header-left-group {
height: 100%;
> div {
padding: 0px;
width: 54px;
height: 54px;
float: left;
text-align: center;
> div:first-child {
filter: invert(0.9);
background: $background-color-1;
border-radius: 0px;
width: 70px;
}
}
> span {
font-size: 10px;
}
.cvat-annotation-header-button {
padding: 0px;
width: 54px;
height: 54px;
float: left;
text-align: center;
user-select: none;
> i {
transform: scale(0.8);
padding: 3px;
}
> span {
font-size: 10px;
}
&:hover > i {
transform: scale(0.9);
}
> i {
transform: scale(0.8);
padding: 3px;
}
&:active > i {
transform: scale(0.8);
}
&:hover > i {
transform: scale(0.9);
}
> * {
display: block;
line-height: 0px;
}
&:active > i {
transform: scale(0.8);
}
> div:first-child {
filter: invert(0.9);
background: $background-color-1;
border-radius: 0px;
width: 70px;
> * {
display: block;
line-height: 0px;
}
}
.cvat-annotation-header-player-group > div {
height: 54px;
line-height: 0px;
}
.cvat-annotation-header-player-buttons {
......@@ -96,14 +98,16 @@
}
.cvat-annotation-header-filename-wrapper {
max-width: 180px;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
}
.cvat-annotation-header-frame-selector {
width: 5em;
margin-right: 0.5em;
padding-right: 5px;
margin-left: 5px;
}
.cvat-annotation-header-right-group {
......
......@@ -34,25 +34,25 @@ export default function AnnotationPageComponent(): JSX.Element {
<Layout.Header className='cvat-annotation-page-header'>
<Row type='flex' justify='space-between'>
<Col className='cvat-annotation-header-left-group'>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={MainMenuIcon} />
<span>Menu</span>
</div>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={SaveIcon} />
<span>Save</span>
</div>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={UndoIcon} />
<span>Undo</span>
</div>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={RedoIcon} />
<span>Redo</span>
</div>
</Col>
<Col className='cvat-annotation-header-player-group'>
<Row type='flex'>
<Row type='flex' align='middle'>
<Col className='cvat-annotation-header-player-buttons'>
<Icon component={PlaycontrolFirstIcon} />
<Icon component={PlaycontrolBackJumpIcon} />
......@@ -68,26 +68,25 @@ export default function AnnotationPageComponent(): JSX.Element {
<Slider className='cvat-annotation-header-player-slider' tipFormatter={null} />
</Col>
</Row>
<Row type='flex' justify='space-between'>
<Row type='flex' justify='space-around'>
<Col className='cvat-annotation-header-filename-wrapper'>
<Tooltip overlay='filename.png'>
<Text type='secondary'>filename.png</Text>
</Tooltip>
</Col>
<Col>
<Input className='cvat-annotation-header-frame-selector' type='number' size='small' />
<Text type='secondary'>of 200</Text>
</Col>
</Row>
</Col>
<Col>
<Input className='cvat-annotation-header-frame-selector' type='number' />
</Col>
</Row>
</Col>
<Col className='cvat-annotation-header-right-group'>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={FullscreenIcon} />
<span>Fullscreen</span>
</div>
<div>
<div className='cvat-annotation-header-button'>
<Icon component={InfoIcon} />
<span>Info</span>
</div>
......
import 'antd/dist/antd.less';
import '../base.scss';
import '../styles.scss';
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import {
......@@ -262,7 +262,7 @@ export default class CVATApplication extends React.PureComponent<CVATAppProps> {
<Route exact path='/tasks' component={TasksPageContainer} />
<Route exact path='/tasks/create' component={CreateTaskPageContainer} />
<Route exact path='/tasks/:id' component={TaskPageContainer} />
<Route exact path='/tasks/:id/jobs/:id' component={AnnotationPageContainer} />
<Route exact path='/tasks/:tid/jobs/:jid' component={AnnotationPageContainer} />
{ withModels
&& <Route exact path='/models' component={ModelsPageContainer} /> }
{ installedAutoAnnotation
......@@ -289,7 +289,7 @@ export default class CVATApplication extends React.PureComponent<CVATAppProps> {
}
return (
<Spin size='large' style={{ margin: '25% 50%' }} />
<Spin size='large' className='cvat-spinner' />
);
}
}
......@@ -407,7 +407,7 @@ export default class ModelRunnerModalComponent extends React.PureComponent<Props
visible
>
{!modelsInitialized
&& <Spin size='large' style={{ margin: '25% 50%' }} />}
&& <Spin size='large' className='cvat-spinner' />}
{modelsInitialized && this.renderContent()}
</Modal>
)
......
......@@ -42,7 +42,7 @@ export default function ModelsPageComponent(props: Props): JSX.Element {
props.getModels();
}
return (
<Spin size='large' style={{ margin: '25% 45%' }} />
<Spin size='large' className='cvat-spinner' />
);
}
......
......@@ -7,7 +7,7 @@ import {
Col,
Row,
Spin,
notification,
Result,
} from 'antd';
import TopBarComponent from './top-bar';
......@@ -17,11 +17,11 @@ import ModelRunnerModalContainer from '../../containers/model-runner-dialog/mode
import { Task } from '../../reducers/interfaces';
interface TaskPageComponentProps {
task: Task | null;
task: Task | null | undefined;
fetching: boolean;
deleteActivity: boolean | null;
installedGit: boolean;
onFetchTask: (tid: number) => void;
getTask: () => void;
}
type Props = TaskPageComponentProps & RouteComponentProps<{id: string}>;
......@@ -38,41 +38,33 @@ class TaskPageComponent extends React.PureComponent<Props> {
if (deleteActivity) {
history.replace('/tasks');
}
if (this.attempts === 2) {
notification.warning({
message: 'Something wrong with the task. It cannot be fetched from the server',
});
}
}
public render(): JSX.Element {
const {
match,
task,
fetching,
onFetchTask,
getTask,
} = this.props;
const { id } = match.params;
const fetchTask = !task;
if (fetchTask) {
if (task === null) {
if (!fetching) {
if (!this.attempts) {
this.attempts++;
onFetchTask(+id);
} else {
this.attempts++;
}
getTask();
}
return (
<Spin size='large' style={{ margin: '25% 50%' }} />
<Spin size='large' className='cvat-spinner' />
);
}
if (typeof (task) === 'undefined') {
return (
<div> </div>
<Result
className='cvat-not-found'
status='404'
title='Sorry, but this task was not found'
subTitle='Please, be sure information you tried to get exist and you have access'
/>
);
}
......
......@@ -207,7 +207,7 @@ class TasksPageComponent extends React.PureComponent<TasksPageProps & RouteCompo
if (tasksFetching) {
return (
<Spin size='large' style={{ margin: '25% 45%' }} />
<Spin size='large' className='cvat-spinner' />
);
}
......
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { RouteComponentProps } from 'react-router';
import AnnotationPageComponent from '../../components/annotation-page/annotation-page';
import { getTasksAsync } from '../../actions/tasks-actions';
export default function AnnotationPageContainer() {
import {
CombinedState,
Task,
} from '../../reducers/interfaces';
type OwnProps = RouteComponentProps<{
tid: string;
jid: string;
}>;
interface StateToProps {
jobInstance: any | null | undefined;
fetching: boolean;
}
interface DispatchToProps {
getJob(): void;
}
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
const { tasks } = state;
const {
gettingQuery,
current,
} = tasks;
const { params } = own.match;
const taskID = +params.tid;
const jobID = +params.jid;
const filteredTasks = current
.filter((_task: Task) => _task.instance.id === taskID);
const task = filteredTasks[0] || (gettingQuery.id === taskID || Number.isNaN(taskID)
? undefined : null);
const job = task ? task.instance.jobs
.filter((_job: any) => _job.id === jobID)[0] : task;
return {
jobInstance: job,
fetching: tasks.fetching,
};
}
function mapDispatchToProps(dispatch: any, own: OwnProps): DispatchToProps {
const { params } = own.match;
const taskID = +params.tid;
return {
getJob(): void {
dispatch(getTasksAsync({
id: taskID,
page: 1,
search: null,
owner: null,
assignee: null,
name: null,
status: null,
mode: null,
}));
},
};
}
function AnnotationPageContainer(props: StateToProps & DispatchToProps): JSX.Element {
return (
<AnnotationPageComponent/>
<AnnotationPageComponent {...props} />
);
}
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(AnnotationPageContainer),
);
......@@ -14,23 +14,29 @@ import {
type Props = RouteComponentProps<{id: string}>;
interface StateToProps {
task: Task | null;
task: Task | null | undefined;
fetching: boolean;
deleteActivity: boolean | null;
installedGit: boolean;
}
interface DispatchToProps {
onFetchTask: (tid: number) => void;
getTask: () => void;
}
function mapStateToProps(state: CombinedState, own: Props): StateToProps {
const { plugins } = state.plugins;
const { deletes } = state.tasks.activities;
const { tasks } = state;
const { gettingQuery } = tasks;
const { deletes } = tasks.activities;
const id = +own.match.params.id;
const filtered = state.tasks.current.filter((task) => task.instance.id === id);
const task = filtered[0] || null;
const filteredTasks = state.tasks.current
.filter((task) => task.instance.id === id);
const task = filteredTasks[0] || (gettingQuery.id === id || Number.isNaN(id)
? undefined : null);
let deleteActivity = null;
if (task && id in deletes.byTask) {
......@@ -45,11 +51,13 @@ function mapStateToProps(state: CombinedState, own: Props): StateToProps {
};
}
function mapDispatchToProps(dispatch: any): DispatchToProps {
function mapDispatchToProps(dispatch: any, own: Props): DispatchToProps {
const id = +own.match.params.id;
return {
onFetchTask: (tid: number): void => {
getTask: (): void => {
dispatch(getTasksAsync({
id: tid,
id,
page: 1,
search: null,
owner: null,
......
@import './base.scss';
hr {
border: none;
border-top: 1px solid $border-color-1;
height: 1px;
}
.cvat-spinner {
margin: 25% 50%;
}
.cvat-not-found {
margin: 10% 25%;
}
.cvat-text-color {
color: $text-color;
}
.cvat-title {
font-weight: 400;
font-size: 21px;
color: $text-color;
padding-top: 5px;
}
#root {
width: 100%;
height: 100%;
display: grid;
min-width: 1280px;
min-height: 768px;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册