You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.4 KiB
TypeScript
197 lines
5.4 KiB
TypeScript
// Copyright (C) 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import React from 'react';
|
|
|
|
import { connect } from 'react-redux';
|
|
import { CombinedState, ContextMenuType } from 'reducers/interfaces';
|
|
|
|
import CanvasContextMenuComponent from 'components/annotation-page/standard-workspace/canvas-context-menu';
|
|
|
|
interface StateToProps {
|
|
activatedStateID: number | null;
|
|
objectStates: any[];
|
|
visible: boolean;
|
|
top: number;
|
|
left: number;
|
|
type: ContextMenuType;
|
|
collapsed: boolean | undefined;
|
|
}
|
|
|
|
function mapStateToProps(state: CombinedState): StateToProps {
|
|
const {
|
|
annotation: {
|
|
annotations: { activatedStateID, collapsed, states: objectStates },
|
|
canvas: {
|
|
contextMenu: {
|
|
visible, top, left, type,
|
|
},
|
|
ready,
|
|
},
|
|
},
|
|
} = state;
|
|
|
|
return {
|
|
activatedStateID,
|
|
collapsed: activatedStateID !== null ? collapsed[activatedStateID] : undefined,
|
|
objectStates,
|
|
visible:
|
|
activatedStateID !== null &&
|
|
visible &&
|
|
ready &&
|
|
objectStates.map((_state: any): number => _state.clientID).includes(activatedStateID),
|
|
left,
|
|
top,
|
|
type,
|
|
};
|
|
}
|
|
|
|
type Props = StateToProps;
|
|
|
|
interface State {
|
|
latestLeft: number;
|
|
latestTop: number;
|
|
left: number;
|
|
top: number;
|
|
}
|
|
|
|
class CanvasContextMenuContainer extends React.PureComponent<Props, State> {
|
|
private initialized: HTMLDivElement | null;
|
|
|
|
private dragging: boolean;
|
|
|
|
private dragInitPosX: number;
|
|
|
|
private dragInitPosY: number;
|
|
|
|
public constructor(props: Props) {
|
|
super(props);
|
|
|
|
this.initialized = null;
|
|
this.dragging = false;
|
|
this.dragInitPosX = 0;
|
|
this.dragInitPosY = 0;
|
|
this.state = {
|
|
latestLeft: 0,
|
|
latestTop: 0,
|
|
left: 0,
|
|
top: 0,
|
|
};
|
|
}
|
|
|
|
static getDerivedStateFromProps(props: Props, state: State): State | null {
|
|
if (props.left === state.latestLeft && props.top === state.latestTop) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
...state,
|
|
latestLeft: props.left,
|
|
latestTop: props.top,
|
|
top: props.top,
|
|
left: props.left,
|
|
};
|
|
}
|
|
|
|
public componentDidMount(): void {
|
|
this.updatePositionIfOutOfScreen();
|
|
window.addEventListener('mousemove', this.moveContextMenu);
|
|
}
|
|
|
|
public componentDidUpdate(prevProps: Props): void {
|
|
const { collapsed } = this.props;
|
|
|
|
const [element] = window.document.getElementsByClassName('cvat-canvas-context-menu');
|
|
if (collapsed !== prevProps.collapsed && element) {
|
|
element.addEventListener(
|
|
'transitionend',
|
|
() => {
|
|
this.updatePositionIfOutOfScreen();
|
|
},
|
|
{ once: true },
|
|
);
|
|
} else if (element) {
|
|
this.updatePositionIfOutOfScreen();
|
|
}
|
|
|
|
if (element && (!this.initialized || this.initialized !== element)) {
|
|
this.initialized = element as HTMLDivElement;
|
|
|
|
this.initialized.addEventListener('mousedown', (e: MouseEvent): any => {
|
|
this.dragging = true;
|
|
this.dragInitPosX = e.clientX;
|
|
this.dragInitPosY = e.clientY;
|
|
});
|
|
|
|
this.initialized.addEventListener('mouseup', () => {
|
|
this.dragging = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
public componentWillUnmount(): void {
|
|
window.removeEventListener('mousemove', this.moveContextMenu);
|
|
}
|
|
|
|
private moveContextMenu = (e: MouseEvent): void => {
|
|
if (this.dragging) {
|
|
this.setState((state) => {
|
|
const value = {
|
|
left: state.left + e.clientX - this.dragInitPosX,
|
|
top: state.top + e.clientY - this.dragInitPosY,
|
|
};
|
|
|
|
this.dragInitPosX = e.clientX;
|
|
this.dragInitPosY = e.clientY;
|
|
|
|
return value;
|
|
});
|
|
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
private updatePositionIfOutOfScreen(): void {
|
|
const { top, left } = this.state;
|
|
|
|
const { innerWidth, innerHeight } = window;
|
|
|
|
const [element] = window.document.getElementsByClassName('cvat-canvas-context-menu');
|
|
if (element) {
|
|
const height = element.clientHeight;
|
|
const width = element.clientWidth;
|
|
|
|
if (top + height > innerHeight || left + width > innerWidth) {
|
|
this.setState({
|
|
top: top - Math.max(top + height - innerHeight, 0),
|
|
left: left - Math.max(left + width - innerWidth, 0),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
public render(): JSX.Element {
|
|
const { left, top } = this.state;
|
|
const {
|
|
visible, activatedStateID, objectStates, type,
|
|
} = this.props;
|
|
|
|
return (
|
|
<>
|
|
{type === ContextMenuType.CANVAS_SHAPE && (
|
|
<CanvasContextMenuComponent
|
|
left={left}
|
|
top={top}
|
|
visible={visible}
|
|
objectStates={objectStates}
|
|
activatedStateID={activatedStateID}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default connect(mapStateToProps)(CanvasContextMenuContainer);
|