Part of forms was updated

main
Boris Sekachev 5 years ago
parent 55f4d95e67
commit 4a0cf6b0f8

@ -4,8 +4,10 @@
import './styles.scss'; import './styles.scss';
import React from 'react'; import React from 'react';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import DumpSubmenu from './dump-submenu'; import DumpSubmenu from './dump-submenu';
import LoadSubmenu from './load-submenu'; import LoadSubmenu from './load-submenu';
@ -22,7 +24,7 @@ interface Props {
exportActivities: string[] | null; exportActivities: string[] | null;
inferenceIsActive: boolean; inferenceIsActive: boolean;
onClickMenu: (params: ClickParam, file?: File) => void; onClickMenu: (params: MenuInfo, file?: File) => void;
} }
export enum Actions { export enum Actions {
@ -48,8 +50,8 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
loadActivity, loadActivity,
} = props; } = props;
let latestParams: ClickParam | null = null; let latestParams: MenuInfo | null = null;
function onClickMenuWrapper(params: ClickParam | null, file?: File): void { function onClickMenuWrapper(params: MenuInfo | null, file?: File): void {
const copyParams = params || latestParams; const copyParams = params || latestParams;
if (!copyParams) { if (!copyParams) {
return; return;
@ -67,7 +69,7 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams, file); onClickMenu(copyParams, file);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Update', okText: 'Update',
}); });
@ -83,7 +85,7 @@ export default function ActionsMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams); onClickMenu(copyParams);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -7,7 +7,7 @@ import { AnyAction } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Radio, { RadioChangeEvent } from 'antd/lib/radio'; import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Slider, { SliderValue } from 'antd/lib/slider'; import Slider from 'antd/lib/slider';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Collapse from 'antd/lib/collapse'; import Collapse from 'antd/lib/collapse';
@ -42,8 +42,8 @@ interface StateToProps {
interface DispatchToProps { interface DispatchToProps {
collapseAppearance(): void; collapseAppearance(): void;
changeShapesColorBy(event: RadioChangeEvent): void; changeShapesColorBy(event: RadioChangeEvent): void;
changeShapesOpacity(event: SliderValue): void; changeShapesOpacity(value: number): void;
changeSelectedShapesOpacity(event: SliderValue): void; changeSelectedShapesOpacity(value: number): void;
changeShapesOutlinedBorders(outlined: boolean, color: string): void; changeShapesOutlinedBorders(outlined: boolean, color: string): void;
changeShowBitmap(event: CheckboxChangeEvent): void; changeShowBitmap(event: CheckboxChangeEvent): void;
changeShowProjections(event: CheckboxChangeEvent): void; changeShowProjections(event: CheckboxChangeEvent): void;
@ -107,11 +107,11 @@ function mapDispatchToProps(dispatch: Dispatch<AnyAction>): DispatchToProps {
changeShapesColorBy(event: RadioChangeEvent): void { changeShapesColorBy(event: RadioChangeEvent): void {
dispatch(changeShapesColorByAction(event.target.value)); dispatch(changeShapesColorByAction(event.target.value));
}, },
changeShapesOpacity(value: SliderValue): void { changeShapesOpacity(value: number): void {
dispatch(changeShapesOpacityAction(value as number)); dispatch(changeShapesOpacityAction(value));
}, },
changeSelectedShapesOpacity(value: SliderValue): void { changeSelectedShapesOpacity(value: number): void {
dispatch(changeSelectedShapesOpacityAction(value as number)); dispatch(changeSelectedShapesOpacityAction(value));
}, },
changeShapesOutlinedBorders(outlined: boolean, color: string): void { changeShapesOutlinedBorders(outlined: boolean, color: string): void {
dispatch(changeShapesOutlinedBordersAction(outlined, color)); dispatch(changeShapesOutlinedBordersAction(outlined, color));

@ -4,7 +4,9 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item'; import ObjectItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/object-item';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
@ -27,7 +29,7 @@ interface ReviewContextMenuProps {
top: number; top: number;
left: number; left: number;
latestComments: string[]; latestComments: string[];
onClick: (param: ClickParam) => void; onClick: (param: MenuInfo) => void;
} }
enum ReviewContextMenuKeys { enum ReviewContextMenuKeys {
@ -95,7 +97,7 @@ export default function CanvasContextMenu(props: Props): JSX.Element | null {
top={top} top={top}
left={left} left={left}
latestComments={latestComments} latestComments={latestComments}
onClick={(param: ClickParam) => { onClick={(param: MenuInfo) => {
const [state] = objectStates.filter( const [state] = objectStates.filter(
(_state: any): boolean => _state.clientID === contextMenuClientID, (_state: any): boolean => _state.clientID === contextMenuClientID,
); );

@ -129,9 +129,9 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
</Col> </Col>
<Col span={10}> <Col span={10}>
<InputNumber <InputNumber
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangePoints(Math.floor(clamp(value, minimumPoints, Number.MAX_SAFE_INTEGER))); onChangePoints(Math.floor(clamp(+value, minimumPoints, Number.MAX_SAFE_INTEGER)));
} else if (!value) { } else if (!value) {
onChangePoints(undefined); onChangePoints(undefined);
} }

@ -531,10 +531,10 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
min={1} min={1}
precision={0} precision={0}
max={jobInstance.stopFrame - frame} max={jobInstance.stopFrame - frame}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value !== 'undefined') { if (typeof value !== 'undefined') {
this.setState({ this.setState({
trackingFrames: value, trackingFrames: +value,
}); });
} }
}} }}

@ -134,9 +134,9 @@ function ItemAttributeComponent(props: Props): JSX.Element {
<InputNumber <InputNumber
disabled={readonly} disabled={readonly}
size='small' size='small'
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changeAttribute(attrID, `${clamp(value, min, max)}`); changeAttribute(attrID, `${clamp(+value, min, max)}`);
} }
}} }}
value={+attrValue} value={+attrValue}

@ -52,10 +52,10 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
size='small' size='small'
min={minPropagateFrames} min={minPropagateFrames}
value={propagateFrames} value={propagateFrames}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changePropagateFrames( changePropagateFrames(
Math.floor(clamp(value, minPropagateFrames, Number.MAX_SAFE_INTEGER)), Math.floor(clamp(+value, minPropagateFrames, Number.MAX_SAFE_INTEGER)),
); );
} }
}} }}
@ -67,9 +67,9 @@ export default function PropagateConfirmComponent(props: Props): JSX.Element {
value={propagateUpToFrame} value={propagateUpToFrame}
min={frameNumber + 1} min={frameNumber + 1}
max={stopFrame} max={stopFrame}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
changeUpToFrame(Math.floor(clamp(value, frameNumber + 1, stopFrame))); changeUpToFrame(Math.floor(clamp(+value, frameNumber + 1, stopFrame)));
} }
}} }}
/> />

@ -3,8 +3,10 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Menu, { ClickParam } from 'antd/lib/menu'; import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal'; import Modal from 'antd/lib/modal';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import DumpSubmenu from 'components/actions-menu/dump-submenu'; import DumpSubmenu from 'components/actions-menu/dump-submenu';
import LoadSubmenu from 'components/actions-menu/load-submenu'; import LoadSubmenu from 'components/actions-menu/load-submenu';
@ -19,7 +21,7 @@ interface Props {
exportActivities: string[] | null; exportActivities: string[] | null;
isReviewer: boolean; isReviewer: boolean;
jobInstance: any; jobInstance: any;
onClickMenu(params: ClickParam, file?: File): void; onClickMenu(params: MenuInfo, file?: File): void;
setForceExitAnnotationFlag(forceExit: boolean): void; setForceExitAnnotationFlag(forceExit: boolean): void;
saveAnnotations(jobInstance: any, afterSave?: () => void): void; saveAnnotations(jobInstance: any, afterSave?: () => void): void;
} }
@ -54,15 +56,15 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
const jobStatus = jobInstance.status; const jobStatus = jobInstance.status;
const taskID = jobInstance.task.id; const taskID = jobInstance.task.id;
let latestParams: ClickParam | null = null; let latestParams: MenuInfo | null = null;
function onClickMenuWrapper(params: ClickParam | null, file?: File): void { function onClickMenuWrapper(params: MenuInfo | null, file?: File): void {
const copyParams = params || latestParams; const copyParams = params || latestParams;
if (!copyParams) { if (!copyParams) {
return; return;
} }
latestParams = params; latestParams = params;
function checkUnsavedChanges(_copyParams: ClickParam): void { function checkUnsavedChanges(_copyParams: MenuInfo): void {
if (jobInstance.annotations.hasUnsavedChanges()) { if (jobInstance.annotations.hasUnsavedChanges()) {
Modal.confirm({ Modal.confirm({
title: 'The job has unsaved annotations', title: 'The job has unsaved annotations',
@ -100,7 +102,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams, file); onClickMenu(copyParams, file);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Update', okText: 'Update',
}); });
@ -118,7 +120,7 @@ export default function AnnotationMenuComponent(props: Props): JSX.Element {
onClickMenu(copyParams); onClickMenu(copyParams);
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -6,9 +6,10 @@ import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import { LinkOutlined } from '@ant-design/icons'; import { LinkOutlined } from '@ant-design/icons';
import Slider, { SliderValue } from 'antd/lib/slider'; import Slider from 'antd/lib/slider';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Input from 'antd/lib/input';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { clamp } from 'utils/math'; import { clamp } from 'utils/math';
@ -19,8 +20,8 @@ interface Props {
frameNumber: number; frameNumber: number;
frameFilename: string; frameFilename: string;
focusFrameInputShortcut: string; focusFrameInputShortcut: string;
inputFrameRef: React.RefObject<InputNumber>; inputFrameRef: React.RefObject<Input>;
onSliderChange(value: SliderValue): void; onSliderChange(value: number): void;
onInputChange(value: number): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
} }
@ -76,12 +77,13 @@ function PlayerNavigation(props: Props): JSX.Element {
<Col> <Col>
<Tooltip title={`Press ${focusFrameInputShortcut} to focus here`} mouseLeaveDelay={0}> <Tooltip title={`Press ${focusFrameInputShortcut} to focus here`} mouseLeaveDelay={0}>
<InputNumber <InputNumber
ref={inputFrameRef}
className='cvat-player-frame-selector' className='cvat-player-frame-selector'
type='number' type='number'
value={frameInputValue} value={frameInputValue}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof (value) !== 'undefined') {
setFrameInputValue(Math.floor(clamp(value, startFrame, stopFrame))); setFrameInputValue(Math.floor(clamp(+value, startFrame, stopFrame)));
} }
}} }}
onBlur={() => { onBlur={() => {
@ -90,7 +92,6 @@ function PlayerNavigation(props: Props): JSX.Element {
onPressEnter={() => { onPressEnter={() => {
onInputChange(frameInputValue); onInputChange(frameInputValue);
}} }}
ref={inputFrameRef}
/> />
</Tooltip> </Tooltip>
</Col> </Col>

@ -5,8 +5,7 @@
import React from 'react'; import React from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import InputNumber from 'antd/lib/input-number'; import Input from 'antd/lib/input';
import { SliderValue } from 'antd/lib/slider';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
import LeftGroup from './left-group'; import LeftGroup from './left-group';
@ -20,7 +19,7 @@ interface Props {
savingStatuses: string[]; savingStatuses: string[];
frameNumber: number; frameNumber: number;
frameFilename: string; frameFilename: string;
inputFrameRef: React.RefObject<InputNumber>; inputFrameRef: React.RefObject<Input>;
startFrame: number; startFrame: number;
stopFrame: number; stopFrame: number;
undoAction?: string; undoAction?: string;
@ -49,7 +48,7 @@ interface Props {
onLastFrame(): void; onLastFrame(): void;
setPrevButtonType(type: 'regular' | 'filtered' | 'empty'): void; setPrevButtonType(type: 'regular' | 'filtered' | 'empty'): void;
setNextButtonType(type: 'regular' | 'filtered' | 'empty'): void; setNextButtonType(type: 'regular' | 'filtered' | 'empty'): void;
onSliderChange(value: SliderValue): void; onSliderChange(value: number): void;
onInputChange(value: number): void; onInputChange(value: number): void;
onURLIconClick(): void; onURLIconClick(): void;
onUndoClick(): void; onUndoClick(): void;

@ -3,12 +3,12 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import { LockOutlined } from '@ant-design/icons'; import { LockOutlined } from '@ant-design/icons';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import patterns from 'utils/validation-patterns'; import { validateConfirmation, validatePassword } from 'components/register-page/register-form';
export interface ChangePasswordData { export interface ChangePasswordData {
oldPassword: string; oldPassword: string;
@ -16,144 +16,65 @@ export interface ChangePasswordData {
newPassword2: string; newPassword2: string;
} }
type ChangePasswordFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(loginData: ChangePasswordData): void; onSubmit(loginData: ChangePasswordData): void;
} & FormComponentProps; }
class ChangePasswordFormComponent extends React.PureComponent<ChangePasswordFormProps> {
private validateConfirmation = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (value && value !== form.getFieldValue('newPassword1')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
}
};
private validatePassword = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message);
}
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message);
}
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message);
}
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message);
}
if (value) {
form.validateFields(['newPassword2'], { force: true });
}
callback();
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
const validatedFields = {
...values,
confirmations: [],
};
onSubmit(validatedFields);
}
});
};
private renderOldPasswordField(): JSX.Element {
const { form } = this.props;
function ChangePasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element {
return ( return (
<Form.Item hasFeedback> <Form onFinish={onSubmit} className='change-password-form'>
{form.getFieldDecorator('oldPassword', { <Form.Item
rules: [ hasFeedback
name='oldPassword'
rules={[
{ {
required: true, required: true,
message: 'Please input your current password!', message: 'Please input your current password!',
}, },
], ]}
})( >
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='current-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Current password' placeholder='Current password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderNewPasswordField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
name='newPassword1'
return ( rules={[
<Form.Item hasFeedback>
{form.getFieldDecorator('newPassword1', {
rules: [
{ {
required: true, required: true,
message: 'Please input new password!', message: 'Please input new password!',
}, }, validatePassword,
{ ]}
validator: this.validatePassword, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='New password' placeholder='New password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderNewPasswordConfirmationField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('newPassword2', { name='newPassword2'
rules: [ dependencies={['newPassword1']}
rules={[
{ {
required: true, required: true,
message: 'Please confirm your new password!', message: 'Please confirm your new password!',
}, }, validateConfirmation('newPassword1'),
{ ]}
validator: this.validateConfirmation, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Confirm new password' placeholder='Confirm new password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='change-password-form'>
{this.renderOldPasswordField()}
{this.renderNewPasswordField()}
{this.renderNewPasswordConfirmationField()}
<Form.Item> <Form.Item>
<Button <Button
@ -168,7 +89,6 @@ class ChangePasswordFormComponent extends React.PureComponent<ChangePasswordForm
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<ChangePasswordFormProps>()(ChangePasswordFormComponent); export default React.memo(ChangePasswordFormComponent);

@ -149,6 +149,7 @@ export default class FileManager extends React.PureComponent<Props, State> {
className='cvat-share-tree' className='cvat-share-tree'
checkable checkable
showLine showLine
height={256}
checkStrictly={false} checkStrictly={false}
expandedKeys={expandedKeys} expandedKeys={expandedKeys}
checkedKeys={files.share} checkedKeys={files.share}

@ -5,10 +5,7 @@
@import '../../base.scss'; @import '../../base.scss';
.cvat-share-tree { .cvat-share-tree {
height: fit-content; height: 32 * $grid-unit-size;
min-height: 10em;
max-height: 20em;
overflow: auto;
} }
.cvat-empty-share-tree { .cvat-empty-share-tree {

@ -89,9 +89,9 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
min={minFrameStep} min={minFrameStep}
max={maxFrameStep} max={maxFrameStep}
value={frameStep} value={frameStep}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeFrameStep(Math.floor(clamp(value, minFrameStep, maxFrameStep))); onChangeFrameStep(Math.floor(clamp(+value, minFrameStep, maxFrameStep)));
} }
}} }}
/> />
@ -174,9 +174,9 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element {
max={maxGridSize} max={maxGridSize}
value={gridSize} value={gridSize}
disabled={!grid} disabled={!grid}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeGridSize(Math.floor(clamp(value, minGridSize, maxGridSize))); onChangeGridSize(Math.floor(clamp(+value, minGridSize, maxGridSize)));
} }
}} }}
/> />

@ -70,10 +70,10 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
max={maxAutoSaveInterval} max={maxAutoSaveInterval}
step={1} step={1}
value={Math.round(autoSaveInterval / (60 * 1000))} value={Math.round(autoSaveInterval / (60 * 1000))}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeAutoSaveInterval( onChangeAutoSaveInterval(
Math.floor(clamp(value, minAutoSaveInterval, maxAutoSaveInterval)) * 60 * 1000, Math.floor(clamp(+value, minAutoSaveInterval, maxAutoSaveInterval)) * 60 * 1000,
); );
} }
}} }}
@ -112,7 +112,8 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col> <Col>
<Text type='secondary'> <Text type='secondary'>
{' '} {' '}
Show text for an object on the canvas not only when the object is activated{' '} Show text for an object on the canvas not only when the object is activated
{' '}
</Text> </Text>
</Col> </Col>
</Row> </Row>
@ -131,7 +132,8 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
<Col> <Col>
<Text type='secondary'> <Text type='secondary'>
{' '} {' '}
Enable automatic bordering for polygons and polylines during drawing/editing{' '} Enable automatic bordering for polygons and polylines during drawing/editing
{' '}
</Text> </Text>
</Col> </Col>
</Row> </Row>
@ -142,9 +144,9 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element {
min={minAAMMargin} min={minAAMMargin}
max={maxAAMMargin} max={maxAAMMargin}
value={aamZoomMargin} value={aamZoomMargin}
onChange={(value: number | undefined): void => { onChange={(value: number | undefined | string): void => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
onChangeAAMZoomMargin(Math.floor(clamp(value, minAAMMargin, maxAAMMargin))); onChangeAAMZoomMargin(Math.floor(clamp(+value, minAAMMargin, maxAAMMargin)));
} }
}} }}
/> />

@ -3,9 +3,11 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Select, { OptionProps, SelectProps } from 'antd/lib/select'; import Select, { SelectProps } from 'antd/lib/select';
// eslint-disable-next-line import/no-extraneous-dependencies
import { OptionData, OptionGroupData } from 'rc-select/lib/interface';
interface Props extends SelectProps { interface Props extends SelectProps<string> {
labels: any[]; labels: any[];
value: any | number | null; value: any | number | null;
onChange: (label: any) => void; onChange: (label: any) => void;
@ -26,11 +28,13 @@ export default function LabelSelector(props: Props): JSX.Element {
{...rest} {...rest}
{...dinamicProps} {...dinamicProps}
showSearch showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => { filterOption={(input: string, option?: OptionData | OptionGroupData) => {
if (option) {
const { children } = option.props; const { children } = option.props;
if (typeof children === 'string') { if (typeof children === 'string') {
return children.toLowerCase().includes(input.toLowerCase()); return children.toLowerCase().includes(input.toLowerCase());
} }
}
return false; return false;
}} }}

@ -467,8 +467,8 @@ class LabelForm extends React.PureComponent<Props, {}> {
<Col offset={1}> <Col offset={1}>
<Tooltip title='Do not save the label and return' mouseLeaveDelay={0}> <Tooltip title='Do not save the label and return' mouseLeaveDelay={0}>
<Button <Button
danger
style={{ width: '150px' }} style={{ width: '150px' }}
type='danger'
onClick={(): void => { onClick={(): void => {
onSubmit(null); onSubmit(null);
}} }}

@ -101,8 +101,8 @@ class RawViewer extends React.PureComponent<Props> {
<Col offset={1}> <Col offset={1}>
<Tooltip title='Do not save the label and return' mouseLeaveDelay={0}> <Tooltip title='Do not save the label and return' mouseLeaveDelay={0}>
<Button <Button
danger
style={{ width: '150px' }} style={{ width: '150px' }}
type='danger'
onClick={(): void => { onClick={(): void => {
form.resetFields(); form.resetFields();
}} }}

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import { UserOutlined, LockOutlined } from '@ant-design/icons'; import { UserOutlined, LockOutlined } from '@ant-design/icons';
@ -13,78 +13,49 @@ export interface LoginData {
password: string; password: string;
} }
type LoginFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(loginData: LoginData): void; onSubmit(loginData: LoginData): void;
} & FormComponentProps; }
class LoginFormComponent extends React.PureComponent<LoginFormProps> {
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
onSubmit(values);
}
});
};
private renderUsernameField(): JSX.Element {
const { form } = this.props;
const { getFieldDecorator } = form;
function LoginFormComponent(props: Props): JSX.Element {
const { fetching, onSubmit } = props;
return ( return (
<Form.Item hasFeedback> <Form onFinish={onSubmit} className='login-form'>
{getFieldDecorator('username', { <Form.Item
rules: [ hasFeedback
name='username'
rules={[
{ {
required: true, required: true,
message: 'Please specify a username', message: 'Please specify a username',
}, },
], ]}
})( >
<Input <Input
autoComplete='username' autoComplete='username'
prefix={<UserOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<UserOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Username' placeholder='Username'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderPasswordField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
const { getFieldDecorator } = form; name='password'
rules={[
return (
<Form.Item hasFeedback>
{getFieldDecorator('password', {
rules: [
{ {
required: true, required: true,
message: 'Please specify a password', message: 'Please specify a password',
}, },
], ]}
})( >
<Input <Input
autoComplete='current-password' autoComplete='current-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Password' placeholder='Password'
type='password' type='password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='login-form'>
{this.renderUsernameField()}
{this.renderPasswordField()}
<Form.Item> <Form.Item>
<Button <Button
@ -99,7 +70,6 @@ class LoginFormComponent extends React.PureComponent<LoginFormProps> {
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<LoginFormProps>()(LoginFormComponent); export default React.memo(LoginFormComponent);

@ -6,7 +6,7 @@ import './styles.scss';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import { CloseCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons'; import { CloseCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import Select, { OptionProps } from 'antd/lib/select'; import Select from 'antd/lib/select';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import Tag from 'antd/lib/tag'; import Tag from 'antd/lib/tag';
@ -14,9 +14,12 @@ import Text from 'antd/lib/typography/Text';
import InputNumber from 'antd/lib/input-number'; import InputNumber from 'antd/lib/input-number';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import notification from 'antd/lib/notification'; import notification from 'antd/lib/notification';
// eslint-disable-next-line import/no-extraneous-dependencies
import { OptionData, OptionGroupData } from 'rc-select/lib/interface';
import { Model, StringObject } from 'reducers/interfaces'; import { Model, StringObject } from 'reducers/interfaces';
import { clamp } from 'utils/math';
import consts from 'consts'; import consts from 'consts';
interface Props { interface Props {
@ -95,18 +98,20 @@ function DetectorRunner(props: Props): JSX.Element {
onChange={onChange} onChange={onChange}
style={{ width: '100%' }} style={{ width: '100%' }}
showSearch showSearch
filterOption={(input: string, option: React.ReactElement<OptionProps>) => { filterOption={(input: string, option?: OptionData | OptionGroupData) => {
if (option) {
const { children } = option.props; const { children } = option.props;
if (typeof children === 'string') { if (typeof children === 'string') {
return children.toLowerCase().includes(input.toLowerCase()); return children.toLowerCase().includes(input.toLowerCase());
} }
}
return false; return false;
}} }}
> >
{labels.map( {labels.map(
(label: string): JSX.Element => ( (label: string): JSX.Element => (
<Select.Option key={label}>{label}</Select.Option> <Select.Option value={label} key={label}>{label}</Select.Option>
), ),
)} )}
</Select> </Select>
@ -138,7 +143,7 @@ function DetectorRunner(props: Props): JSX.Element {
> >
{models.map( {models.map(
(_model: Model): JSX.Element => ( (_model: Model): JSX.Element => (
<Select.Option key={_model.id}>{_model.name}</Select.Option> <Select.Option value={_model.id} key={_model.id}>{_model.name}</Select.Option>
), ),
)} )}
</Select> </Select>
@ -176,13 +181,19 @@ function DetectorRunner(props: Props): JSX.Element {
<> <>
<Row type='flex' justify='start' align='middle'> <Row type='flex' justify='start' align='middle'>
<Col span={10}> <Col span={10}>
{renderSelector(match.model || '', 'Model labels', modelLabels, (modelLabel: string) => {renderSelector(
updateMatch(modelLabel, null), match.model || '',
'Model labels',
modelLabels,
(modelLabel: string) => updateMatch(modelLabel, null),
)} )}
</Col> </Col>
<Col span={10} offset={1}> <Col span={10} offset={1}>
{renderSelector(match.task || '', 'Task labels', taskLabels, (taskLabel: string) => {renderSelector(
updateMatch(null, taskLabel), match.task || '',
'Task labels',
taskLabels,
(taskLabel: string) => updateMatch(null, taskLabel),
)} )}
</Col> </Col>
<Col span={1} offset={1}> <Col span={1} offset={1}>
@ -219,9 +230,9 @@ function DetectorRunner(props: Props): JSX.Element {
step={0.01} step={0.01}
max={1} max={1}
value={threshold} value={threshold}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
setThreshold(value); setThreshold(clamp(+value, 0.01, 1));
} }
}} }}
/> />
@ -238,9 +249,9 @@ function DetectorRunner(props: Props): JSX.Element {
placeholder='Threshold' placeholder='Threshold'
min={1} min={1}
value={distance} value={distance}
onChange={(value: number | undefined) => { onChange={(value: number | undefined | string) => {
if (typeof value === 'number') { if (typeof value !== 'undefined') {
setDistance(value); setDistance(+value);
} }
}} }}
/> />
@ -258,9 +269,7 @@ function DetectorRunner(props: Props): JSX.Element {
runInference( runInference(
task, task,
model, model,
model.type === 'detector' model.type === 'detector' ? { mapping, cleanup } : {
? { mapping, cleanup }
: {
threshold, threshold,
max_distance: distance, max_distance: distance,
}, },

@ -26,7 +26,7 @@ export default function ProjectActionsMenuComponent(props: Props): JSX.Element {
dispatch(deleteProjectAsync(projectInstance)); dispatch(deleteProjectAsync(projectInstance));
}, },
okButtonProps: { okButtonProps: {
type: 'danger', danger: true,
}, },
okText: 'Delete', okText: 'Delete',
}); });

@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form';
import { UserAddOutlined, MailOutlined, LockOutlined } from '@ant-design/icons'; import { UserAddOutlined, MailOutlined, LockOutlined } from '@ant-design/icons';
import Form, { RuleRender, RuleObject } from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import Checkbox from 'antd/lib/checkbox'; import Checkbox from 'antd/lib/checkbox';
@ -29,175 +29,153 @@ export interface RegisterData {
confirmations: UserConfirmation[]; confirmations: UserConfirmation[];
} }
type RegisterFormProps = { interface Props {
fetching: boolean; fetching: boolean;
userAgreements: UserAgreement[]; userAgreements: UserAgreement[];
onSubmit(registerData: RegisterData): void; onSubmit(registerData: RegisterData): void;
} & FormComponentProps; }
function validateUsername(_: RuleObject, value: string): Promise<void> {
if (!patterns.validateUsernameLength.pattern.test(value)) {
return Promise.reject(new Error(patterns.validateUsernameLength.message));
}
class RegisterFormComponent extends React.PureComponent<RegisterFormProps> { if (!patterns.validateUsernameCharacters.pattern.test(value)) {
private validateConfirmation = (rule: any, value: any, callback: any): void => { return Promise.reject(new Error(patterns.validateUsernameCharacters.message));
const { form } = this.props;
if (value && value !== form.getFieldValue('password1')) {
callback('Two passwords that you enter is inconsistent!');
} else {
callback();
} }
};
private validatePassword = (_: any, value: any, callback: any): void => { return Promise.resolve();
const { form } = this.props; }
export const validatePassword: RuleRender = (): RuleObject => ({
validator(_: RuleObject, value: string): Promise<void> {
if (!patterns.validatePasswordLength.pattern.test(value)) { if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message); return Promise.reject(new Error(patterns.validatePasswordLength.message));
} }
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) { if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message); return Promise.reject(new Error(patterns.passwordContainsNumericCharacters.message));
} }
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) { if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message); return Promise.reject(new Error(patterns.passwordContainsUpperCaseCharacter.message));
} }
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) { if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message); return Promise.reject(new Error(patterns.passwordContainsLowerCaseCharacter.message));
}
if (value) {
form.validateFields(['password2'], { force: true });
} }
callback();
};
private validateUsername = (_: any, value: any, callback: any): void => {
if (!patterns.validateUsernameLength.pattern.test(value)) {
callback(patterns.validateUsernameLength.message);
}
if (!patterns.validateUsernameCharacters.pattern.test(value)) {
callback(patterns.validateUsernameCharacters.message);
}
callback();
};
private validateAgrement = (agreement: any, value: any, callback: any): void => {
const { userAgreements } = this.props;
let isValid = true;
for (const userAgreement of userAgreements) {
if (agreement.field === userAgreement.name && userAgreement.required && !value) {
isValid = false;
callback(`You must accept the ${userAgreement.displayText} to continue!`);
break;
}
}
if (isValid) {
callback();
}
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit, userAgreements } = this.props;
form.validateFields((error, values): void => { return Promise.resolve();
if (!error) { },
const validatedFields = { });
...values,
confirmations: [], export const validateConfirmation: ((firstFieldName: string) => RuleRender) = (
}; firstFieldName: string,
): RuleRender => ({ getFieldValue }): RuleObject => ({
for (const userAgreement of userAgreements) { validator(_: RuleObject, value: string): Promise<void> {
validatedFields.confirmations.push({ if (value && value !== getFieldValue(firstFieldName)) {
name: userAgreement.name, return Promise.reject(new Error('Two passwords that you enter is inconsistent!'));
value: validatedFields[userAgreement.name],
});
delete validatedFields[userAgreement.name];
} }
onSubmit(validatedFields); return Promise.resolve();
},
});
const validateAgreement: ((userAgreements: UserAgreement[]) => RuleRender) = (
userAgreements: UserAgreement[],
): RuleRender => () => ({
validator(rule: any, value: boolean): Promise<void> {
const [, name] = rule.field.split(':');
const [agreement] = userAgreements
.filter((userAgreement: UserAgreement): boolean => userAgreement.name === name);
if (agreement.required && !value) {
return Promise.reject(new Error(`You must accept ${agreement.displayText} to continue!`));
} }
});
};
private renderFirstNameField(): JSX.Element { return Promise.resolve();
const { form } = this.props; },
});
function RegisterFormComponent(props: Props): JSX.Element {
const { fetching, userAgreements, onSubmit } = props;
return ( return (
<Form.Item hasFeedback> <Form
{form.getFieldDecorator('firstName', { onFinish={(values: Record<string, string | boolean>) => {
rules: [ const agreements = Object.keys(values)
.filter((key: string):boolean => key.startsWith('agreement:'));
const confirmations = agreements
.map((key: string): UserConfirmation => ({ name: key.split(':')[1], value: (values[key] as boolean) }));
const rest = Object.entries(values)
.filter((entry: (string | boolean)[]) => !agreements.includes(entry[0] as string));
onSubmit({
...(Object.fromEntries(rest) as any as RegisterData),
confirmations,
});
}}
className='register-form'
>
<Row gutter={8}>
<Col span={12}>
<Form.Item
hasFeedback
name='firstName'
rules={[
{ {
required: true, required: true,
message: 'Please specify a first name', message: 'Please specify a first name',
pattern: patterns.validateName.pattern, pattern: patterns.validateName.pattern,
}, },
], ]}
})( >
<Input <Input
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='First name' placeholder='First name'
/>, />
)}
</Form.Item> </Form.Item>
); </Col>
} <Col span={12}>
<Form.Item
private renderLastNameField(): JSX.Element { hasFeedback
const { form } = this.props; name='lastName'
rules={[
return (
<Form.Item hasFeedback>
{form.getFieldDecorator('lastName', {
rules: [
{ {
required: true, required: true,
message: 'Please specify a last name', message: 'Please specify a last name',
pattern: patterns.validateName.pattern, pattern: patterns.validateName.pattern,
}, },
], ]}
})( >
<Input <Input
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Last name' placeholder='Last name'
/>, />
)}
</Form.Item> </Form.Item>
); </Col>
} </Row>
<Form.Item
private renderUsernameField(): JSX.Element { hasFeedback
const { form } = this.props; name='username'
rules={[
return (
<Form.Item hasFeedback>
{form.getFieldDecorator('username', {
rules: [
{ {
required: true, required: true,
message: 'Please specify a username', message: 'Please specify a username',
}, },
{ {
validator: this.validateUsername, validator: validateUsername,
}, },
], ]}
})( >
<Input <Input
prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<UserAddOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Username' placeholder='Username'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderEmailField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('email', { name='email'
rules: [ rules={[
{ {
type: 'email', type: 'email',
message: 'The input is not valid E-mail!', message: 'The input is not valid E-mail!',
@ -206,120 +184,71 @@ class RegisterFormComponent extends React.PureComponent<RegisterFormProps> {
required: true, required: true,
message: 'Please specify an email address', message: 'Please specify an email address',
}, },
], ]}
})( >
<Input <Input
autoComplete='email' autoComplete='email'
prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Email address' placeholder='Email address'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderPasswordField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('password1', { name='password1'
rules: [ rules={[
{ {
required: true, required: true,
message: 'Please input your password!', message: 'Please input your password!',
}, }, validatePassword,
{ ]}
validator: this.validatePassword, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Password' placeholder='Password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderPasswordConfirmationField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('password2', { name='password2'
rules: [ dependencies={['password1']}
rules={[
{ {
required: true, required: true,
message: 'Please confirm your password!', message: 'Please confirm your password!',
}, }, validateConfirmation('password1'),
{ ]}
validator: this.validateConfirmation, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Confirm password' placeholder='Confirm password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderUserAgreements(): JSX.Element[] { {userAgreements.map((userAgreement: UserAgreement): JSX.Element => (
const { form, userAgreements } = this.props; <Form.Item
const getUserAgreementsElements = (): JSX.Element[] => { name={`agreement:${userAgreement.name}`}
const agreementsList: JSX.Element[] = []; key={userAgreement.name}
for (const userAgreement of userAgreements) { initialValue={false}
agreementsList.push( valuePropName='checked'
<Form.Item key={userAgreement.name}> rules={[
{form.getFieldDecorator(userAgreement.name, {
initialValue: false,
valuePropName: 'checked',
rules: [
{ {
required: true, required: true,
message: 'You must accept to continue!', message: 'You must accept to continue!',
}, }, validateAgreement(userAgreements),
{ ]}
validator: this.validateAgrement, >
},
],
})(
<Checkbox> <Checkbox>
I read and accept the I read and accept the
<a rel='noopener noreferrer' target='_blank' href={userAgreement.url}> <a rel='noopener noreferrer' target='_blank' href={userAgreement.url}>
{` ${userAgreement.displayText}`} {` ${userAgreement.displayText}`}
</a> </a>
</Checkbox>, </Checkbox>
)} </Form.Item>
</Form.Item>, ))}
);
}
return agreementsList;
};
return getUserAgreementsElements();
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='login-form'>
<Row gutter={8}>
<Col span={12}>{this.renderFirstNameField()}</Col>
<Col span={12}>{this.renderLastNameField()}</Col>
</Row>
{this.renderUsernameField()}
{this.renderEmailField()}
{this.renderPasswordField()}
{this.renderPasswordConfirmationField()}
{this.renderUserAgreements()}
<Form.Item> <Form.Item>
<Button <Button
@ -334,7 +263,6 @@ class RegisterFormComponent extends React.PureComponent<RegisterFormProps> {
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<RegisterFormProps>()(RegisterFormComponent); export default React.memo(RegisterFormComponent);

@ -3,14 +3,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import { LockOutlined } from '@ant-design/icons'; import { LockOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import patterns from 'utils/validation-patterns'; import { validateConfirmation, validatePassword } from 'components/register-page/register-form';
export interface ResetPasswordConfirmData { export interface ResetPasswordConfirmData {
newPassword1: string; newPassword1: string;
@ -19,125 +18,59 @@ export interface ResetPasswordConfirmData {
token: string; token: string;
} }
type ResetPasswordConfirmFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(resetPasswordConfirmData: ResetPasswordConfirmData): void; onSubmit(resetPasswordConfirmData: ResetPasswordConfirmData): void;
} & FormComponentProps & RouteComponentProps; }
class ResetPasswordConfirmFormComponent extends React.PureComponent<ResetPasswordConfirmFormProps> {
private validateConfirmation = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (value && value !== form.getFieldValue('newPassword1')) {
callback('Passwords do not match!');
} else {
callback();
}
};
private validatePassword = (_: any, value: string, callback: Function): void => {
const { form } = this.props;
if (!patterns.validatePasswordLength.pattern.test(value)) {
callback(patterns.validatePasswordLength.message);
}
if (!patterns.passwordContainsNumericCharacters.pattern.test(value)) {
callback(patterns.passwordContainsNumericCharacters.message);
}
if (!patterns.passwordContainsUpperCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsUpperCaseCharacter.message);
}
if (!patterns.passwordContainsLowerCaseCharacter.pattern.test(value)) {
callback(patterns.passwordContainsLowerCaseCharacter.message);
}
if (value) {
form.validateFields(['newPassword2'], { force: true });
}
callback();
};
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit, location } = this.props;
function ResetPasswordConfirmFormComponent({ fetching, onSubmit }: Props): JSX.Element {
const location = useLocation();
return (
<Form
onFinish={(values: Record<string, string>): void => {
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
const uid = params.get('uid'); const uid = params.get('uid');
const token = params.get('token'); const token = params.get('token');
if (uid && token) {
form.validateFields((error, values): void => { onSubmit(({ ...values, uid, token } as ResetPasswordConfirmData));
if (!error) {
const validatedFields = {
...values,
uid,
token,
};
onSubmit(validatedFields);
} }
}); }}
}; className='cvat-reset-password-confirm-form'
>
private renderNewPasswordField(): JSX.Element { <Form.Item
const { form } = this.props; hasFeedback
name='newPassword1'
return ( rules={[
<Form.Item hasFeedback>
{form.getFieldDecorator('newPassword1', {
rules: [
{ {
required: true, required: true,
message: 'Please input new password!', message: 'Please input new password!',
}, }, validatePassword,
{ ]}
validator: this.validatePassword, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='New password' placeholder='New password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
private renderNewPasswordConfirmationField(): JSX.Element {
const { form } = this.props;
return ( <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('newPassword2', { name='newPassword2'
rules: [ dependencies={['newPassword1']}
rules={[
{ {
required: true, required: true,
message: 'Please confirm your new password!', message: 'Please confirm your new password!',
}, }, validateConfirmation('newPassword1'),
{ ]}
validator: this.validateConfirmation, >
},
],
})(
<Input.Password <Input.Password
autoComplete='new-password' autoComplete='new-password'
prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<LockOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Confirm new password' placeholder='Confirm new password'
/>, />
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='cvat-reset-password-confirm-form'>
{this.renderNewPasswordField()}
{this.renderNewPasswordConfirmationField()}
<Form.Item> <Form.Item>
<Button <Button
@ -152,7 +85,6 @@ class ResetPasswordConfirmFormComponent extends React.PureComponent<ResetPasswor
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default withRouter(Form.create<ResetPasswordConfirmFormProps>()(ResetPasswordConfirmFormComponent)); export default React.memo(ResetPasswordConfirmFormComponent);

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React from 'react';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import { MailOutlined } from '@ant-design/icons'; import { MailOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
@ -12,30 +12,18 @@ export interface ResetPasswordData {
email: string; email: string;
} }
type ResetPasswordFormProps = { interface Props {
fetching: boolean; fetching: boolean;
onSubmit(resetPasswordData: ResetPasswordData): void; onSubmit(resetPasswordData: ResetPasswordData): void;
} & FormComponentProps; }
class ResetPasswordFormComponent extends React.PureComponent<ResetPasswordFormProps> {
private handleSubmit = (e: React.FormEvent): void => {
e.preventDefault();
const { form, onSubmit } = this.props;
form.validateFields((error, values): void => {
if (!error) {
onSubmit(values);
}
});
};
private renderEmailField(): JSX.Element {
const { form } = this.props;
function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element {
return ( return (
<Form.Item hasFeedback> <Form onFinish={onSubmit} className='cvat-reset-password-form'>
{form.getFieldDecorator('email', { <Form.Item
rules: [ hasFeedback
name='email'
rules={[
{ {
type: 'email', type: 'email',
message: 'The input is not valid E-mail!', message: 'The input is not valid E-mail!',
@ -44,23 +32,14 @@ class ResetPasswordFormComponent extends React.PureComponent<ResetPasswordFormPr
required: true, required: true,
message: 'Please specify an email address', message: 'Please specify an email address',
}, },
], ]}
})( >
<Input <Input
autoComplete='email' autoComplete='email'
prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />} prefix={<MailOutlined style={{ color: 'rgba(0, 0, 0, 0.25)' }} />}
placeholder='Email address' placeholder='Email address'
/>, />
)}
</Form.Item> </Form.Item>
);
}
public render(): JSX.Element {
const { fetching } = this.props;
return (
<Form onSubmit={this.handleSubmit} className='cvat-reset-password-form'>
{this.renderEmailField()}
<Form.Item> <Form.Item>
<Button <Button
@ -75,7 +54,6 @@ class ResetPasswordFormComponent extends React.PureComponent<ResetPasswordFormPr
</Form.Item> </Form.Item>
</Form> </Form>
); );
}
} }
export default Form.create<ResetPasswordFormProps>()(ResetPasswordFormComponent); export default React.memo(ResetPasswordFormComponent);

@ -47,7 +47,9 @@ export default function AutomaticAnnotationProgress(props: Props): JSX.Element |
Modal.confirm({ Modal.confirm({
title: 'You are going to cancel automatic annotation?', title: 'You are going to cancel automatic annotation?',
content: 'Reached progress will be lost. Continue?', content: 'Reached progress will be lost. Continue?',
okType: 'danger', okButtonProps: {
danger: true,
},
onOk() { onOk() {
cancelAutoAnnotation(); cancelAutoAnnotation();
}, },

@ -10,7 +10,8 @@ import { CombinedState } from 'reducers/interfaces';
import { modelsActions } from 'actions/models-actions'; import { modelsActions } from 'actions/models-actions';
import { dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync } from 'actions/tasks-actions'; import { dumpAnnotationsAsync, loadAnnotationsAsync, exportDatasetAsync, deleteTaskAsync } from 'actions/tasks-actions';
import { ClickParam } from 'antd/lib/menu'; // eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
interface OwnProps { interface OwnProps {
taskInstance: any; taskInstance: any;
@ -89,7 +90,7 @@ function ActionsMenuContainer(props: OwnProps & StateToProps & DispatchToProps):
openRunModelWindow, openRunModelWindow,
} = props; } = props;
function onClickMenu(params: ClickParam, file?: File): void { function onClickMenu(params: MenuInfo, file?: File): void {
if (params.keyPath.length > 1) { if (params.keyPath.length > 1) {
const [additionalKey, action] = params.keyPath; const [additionalKey, action] = params.keyPath;
if (action === Actions.DUMP_TASK_ANNO) { if (action === Actions.DUMP_TASK_ANNO) {

@ -5,7 +5,8 @@
import React from 'react'; import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router'; import { withRouter, RouteComponentProps } from 'react-router';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ClickParam } from 'antd/lib/menu/index'; // eslint-disable-next-line import/no-extraneous-dependencies
import { MenuInfo } from 'rc-menu/lib/interface';
import { CombinedState, TaskStatus } from 'reducers/interfaces'; import { CombinedState, TaskStatus } from 'reducers/interfaces';
import AnnotationMenuComponent, { Actions } from 'components/annotation-page/top-bar/annotation-menu'; import AnnotationMenuComponent, { Actions } from 'components/annotation-page/top-bar/annotation-menu';
@ -120,7 +121,7 @@ function AnnotationMenuContainer(props: Props): JSX.Element {
updateJob, updateJob,
} = props; } = props;
const onClickMenu = (params: ClickParam, file?: File): void => { const onClickMenu = (params: MenuInfo, file?: File): void => {
if (params.keyPath.length > 1) { if (params.keyPath.length > 1) {
const [additionalKey, action] = params.keyPath; const [additionalKey, action] = params.keyPath;
if (action === Actions.DUMP_TASK_ANNO) { if (action === Actions.DUMP_TASK_ANNO) {

@ -8,8 +8,7 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps } from 'react-router-dom';
import { GlobalHotKeys, ExtendedKeyMapOptions } from 'react-hotkeys'; import { GlobalHotKeys, ExtendedKeyMapOptions } from 'react-hotkeys';
import InputNumber from 'antd/lib/input-number'; import Input from 'antd/lib/input';
import { SliderValue } from 'antd/lib/slider';
import { import {
changeFrameAsync, changeFrameAsync,
@ -154,15 +153,13 @@ interface State {
type Props = StateToProps & DispatchToProps & RouteComponentProps; type Props = StateToProps & DispatchToProps & RouteComponentProps;
class AnnotationTopBarContainer extends React.PureComponent<Props, State> { class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
private inputFrameRef: React.RefObject<InputNumber>; private inputFrameRef: React.RefObject<Input>;
private autoSaveInterval: number | undefined; private autoSaveInterval: number | undefined;
private unblock: any; private unblock: any;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.inputFrameRef = React.createRef<InputNumber>(); this.inputFrameRef = React.createRef<Input>();
this.state = { this.state = {
prevButtonType: 'regular', prevButtonType: 'regular',
nextButtonType: 'regular', nextButtonType: 'regular',
@ -408,7 +405,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onSaveAnnotation(jobInstance); onSaveAnnotation(jobInstance);
}; };
private onChangePlayerSliderValue = (value: SliderValue): void => { private onChangePlayerSliderValue = (value: number): void => {
const { playing, onSwitchPlay } = this.props; const { playing, onSwitchPlay } = this.props;
if (playing) { if (playing) {
onSwitchPlay(false); onSwitchPlay(false);

Loading…
Cancel
Save