Commit 565e4340 authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza

Added TreeExplorer

parent d49d7d34
import * as React from 'react';
import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
import { WithTranslation, withTranslation } from 'react-i18next';
import ConfiguredTree from 'crop/ui/c/ConfiguredTree';
// import Button from '@material-ui/core/Button/Button';
import * as classnames from 'classnames';
import { throttle } from 'lodash';
import Card, { CardContent, CardHeader } from 'ui/common/Card';
import IconButton from '@material-ui/core/IconButton/IconButton';
import CloseIcon from '@material-ui/icons/Close';
const styles = (theme) => createStyles({
treeWrapper: {
'direction': 'ltr',
'fontSize': '1.2rem',
'flexGrow': 2,
'display': 'flex',
'position': 'relative',
'& > div': {
flexGrow: 2,
},
'& foreignObject': {
position: 'relative',
overflow: 'visible',
},
},
popup: {
position: 'absolute',
backgroundColor: 'white',
border: 'solid 2px grey',
visibility: 'hidden',
display: 'flex',
flexDirection: 'column',
},
popupData: {
fontSize: '1rem',
},
popupTitle: {
textAlign: 'center',
},
visible: {
visibility: 'visible',
},
node: {
position: 'absolute',
// width: 'calc(90% - 20px)',
width: 'max-content',
textAlign: 'right',
// left: 'calc(-90% + 40px)',
right: '-10px',
},
leafNode: {
position: 'absolute',
},
label: {
display: 'inline-block',
verticalAlign: 'top',
whiteSpace: 'nowrap',
},
accessionList: {
width: '400px',
position: 'absolute',
right: 0,
[theme.breakpoints.down('md')]: {
width: '300px',
},
[theme.breakpoints.down('xs')]: {
width: '100vw',
},
},
cardTitle: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
},
});
class NodeLabel extends React.PureComponent<any, any> {
private timeout: any = null;
public state = {
count: 0,
};
private onClick = (e) => {
const { nodeData, clickHandler } = this.props;
if (this.state.count === 1) {
console.log('expand!');
clearTimeout(this.timeout);
this.setState({ count: 0 });
return;
}
const x = e.clientX;
const y = e.clientY;
e.stopPropagation();
this.setState({ count: 1 });
this.timeout = setTimeout(() => {
clickHandler(nodeData, x, y);
this.setState({ count: 0 });
}, 200);
};
public render() {
const { nodeData, classes } = this.props;
return (
<div className={ nodeData._children ? classes.node : classes.leafNode }>
<h6 className={ `pl-20 pr-20 m-0 ${classes.label}`} onClick={ this.onClick }>
{ nodeData.name }
</h6>
</div>
)
}
}
interface ITreeExplorerProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
treeData: any;
}
class TreeExplorer extends React.Component<ITreeExplorerProps, any> {
private readonly treeWrapperRef: React.RefObject<HTMLDivElement>;
private readonly popupRef: React.RefObject<HTMLDivElement>;
private LABEL_HEIGHT = 16;
public constructor(props: ITreeExplorerProps, context: any) {
super(props, context);
this.treeWrapperRef = React.createRef();
this.popupRef = React.createRef();
}
public state = {
x: 0,
y: 0,
popupData: null,
accessionListIsOpen: false,
};
public componentDidMount(): void {
if (typeof window !== 'undefined') {
window.addEventListener('wheel', this.throttledHide);
}
}
public componentWillUnmount(): void {
if (typeof window !== 'undefined') {
window.removeEventListener('wheel', this.throttledHide);
}
}
private handleClick = (nodeData, x, y) => {
const rect = this.treeWrapperRef.current;
const popup = this.popupRef.current;
this.setState({
x: x - popup.getBoundingClientRect().width / 2,
y: y - rect.getBoundingClientRect().top + this.LABEL_HEIGHT,
popupData: { name: nodeData.name, nodeKey: nodeData.fullID },
});
console.log('custom handler!', nodeData, x, y, rect.getBoundingClientRect(), popup.getBoundingClientRect());
};
private hidePopup = () => {
this.setState({
x: 0,
y: 0,
popupData: null,
});
};
private throttledHide = throttle(this.hidePopup, 1000);
private showList = (e) => {
console.log('show list click!');
// e.stopPropagation();
this.setState({ accessionListIsOpen: true });
};
private closeAccessionList = () => {
this.setState({ accessionListIsOpen: false })
};
public render() {
const { treeData, classes } = this.props;
const { x, y, popupData, accessionListIsOpen } = this.state;
// todo: translations
return (
<div className={ classes.treeWrapper } ref={ this.treeWrapperRef }>
<ConfiguredTree
treeData={ [treeData] }
initialZoom={ 0.25 }
zoomable
nodeLabelComponent={{
render: <NodeLabel clickHandler={ this.handleClick } classes={ classes }/>,
foreignObjectWrapper: {
y: -(this.LABEL_HEIGHT / 2),
x: -10,
height: this.LABEL_HEIGHT,
width: 10,
},
}}
// onClickHandler={ this.onNodeClick }
/>
<div
className={ classnames({ [classes.visible]: !!popupData, [classes.popup]: true }) }
style={ { top: y, left: x } }
ref={ this.popupRef }
>
<div className={ classes.popupData }>
{ popupData &&
<>
<div className={ classes.popupTitle }>{ popupData.name }</div>
<div className="mb-1rem">Node key: { popupData.nodeKey }</div>
</>
}
</div>
{ /* fixme: Button causes fontSize issues in header */ }
<div onClick={ this.showList }>Accessions List</div>
</div>
{ accessionListIsOpen &&
<Card className={ classes.accessionList }>
<CardHeader
title={
<div className={ classes.cardTitle }>
<div>
Accession list
</div>
<div className={ classes.buttons }>
<IconButton onClick={ this.closeAccessionList }>
<CloseIcon/>
</IconButton>
</div>
</div>
}
/>
<CardContent>
TEST
</CardContent>
</Card>
}
</div>
);
}
}
export default withStyles(styles)(withTranslation()(TreeExplorer));
import * as React from 'react';
// import { createStyles, withStyles } from '@material-ui/core/styles';
//
// const styles = createStyles({
// leafNode: {},
// node: {},
// });
let Tree;
......@@ -13,45 +7,11 @@ interface IConfiguredTreeProps extends React.ClassAttributes<any> {
initialZoom: number;
zoomable?: boolean;
onClickHandler?: any;
}
class NodeLabel extends React.PureComponent<any, any> {
private timeout: any = null;
public state = {
count: 0,
};
private onClick = (e) => {
const { nodeData } = this.props;
if (this.state.count === 1) {
console.log('expand!');
clearTimeout(this.timeout);
this.setState({ count: 0 });
return;
}
e.stopPropagation();
this.setState({ count: 1 });
this.timeout = setTimeout(() => {
console.log('custom handler!', nodeData);
this.setState({ count: 0 });
}, 300);
};
public render() {
const { nodeData } = this.props;
return (
<div onClick={ this.onClick }>
<h6 className="pl-20 pr-20 m-0" >{ nodeData.name }</h6>
</div>
)
}
nodeLabelComponent?: { render: React.ReactNode, foreignObjectWrapper: Record<string, number | string> };
}
class ConfiguredTree extends React.Component<IConfiguredTreeProps, any> {
private treeContainer = null;
// private timeout: any = null;
public constructor(props, context) {
super(props, context);
......@@ -61,7 +21,6 @@ class ConfiguredTree extends React.Component<IConfiguredTreeProps, any> {
}
public state = {
count: 0,
translate: null,
};
......@@ -78,13 +37,12 @@ class ConfiguredTree extends React.Component<IConfiguredTreeProps, any> {
}
public render() {
const { treeData, initialZoom, zoomable = false } = this.props;
const { treeData, initialZoom, zoomable = false, nodeLabelComponent } = this.props;
return (
<div style={ { height: '100%' } } ref={ (ref) => this.treeContainer = ref } /* onClickCapture={ this.test } */>
<div style={ { height: '100%' } } ref={ (ref) => this.treeContainer = ref }>
{ treeData && typeof window !== 'undefined' &&
<Tree
// onClick={ this.onNodeClick }
separation={ {
siblings: 0.25,
nonSiblings: 1,
......@@ -106,20 +64,20 @@ class ConfiguredTree extends React.Component<IConfiguredTreeProps, any> {
circle: {
fill: '#88ba42',
},
// name: {
// strokeWidth: 0.4,
// transform: 'translate(-13px, -1px)',
// textAnchor: 'end',
// },
name: {
strokeWidth: 0.4,
transform: 'translate(-13px, -1px)',
textAnchor: 'end',
},
},
leafNode: {
circle: {
fill: '#777777',
},
// name: {
// strokeWidth: 0.4,
// transform: 'translate(13px)',
// },
name: {
strokeWidth: 0.4,
transform: 'translate(13px)',
},
},
},
} }
......@@ -130,15 +88,8 @@ class ConfiguredTree extends React.Component<IConfiguredTreeProps, any> {
textAnchor: 'start',
y: 0,
} }
allowForeignObjects
nodeLabelComponent={{
render: <NodeLabel />,
foreignObjectWrapper: {
y: -6,
x: -10,
height: '1.1rem',
},
}}
allowForeignObjects={ !!nodeLabelComponent }
nodeLabelComponent={ nodeLabelComponent }
/>
}
</div>
......
......@@ -33,7 +33,7 @@ import BackButton from 'ui/common/buttons/BackButton';
import DiversityTreeDisplay from './c/DiversityTreeDisplay';
import Tabs, { Tab } from 'ui/common/Tabs';
import RepositoryFile from 'model/repository/RepositoryFile';
import ConfiguredTree from 'crop/ui/c/ConfiguredTree';
import TreeExplorer from 'divtree/ui/c/TreeExplorer';
const styles = () => createStyles({
layoutOverrides: {
......@@ -50,15 +50,6 @@ const styles = () => createStyles({
},
},
},
treeWrapper: {
'direction': 'ltr',
'fontSize': '1.2rem',
'flexGrow': 2,
'display': 'flex',
'& > div': {
flexGrow: 2,
},
},
});
interface IDisplayPageProps extends React.ClassAttributes<any>, WithTranslation, WithStyles {
......@@ -94,7 +85,7 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
];
public constructor(props: IDisplayPageProps, context: any) {
super(props, context);
super(props, context);
}
public state = {
......@@ -198,7 +189,7 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
<div>
{ error && <ErrorMessage error={ error }/> }
{ divtree &&
<DiversityTreeDisplay
<DiversityTreeDisplay
isActionsActive={ isActionsActive }
divtree={ divtree }
accessions={ accessionRefs }
......@@ -210,20 +201,13 @@ class DisplayPage extends React.Component<IDisplayPageProps, any> {
approveDiversityTree={ approveDiversityTree }
deleteDiversityTree={ deleteDiversityTree }
addAllToMyList={ this.props.addDiversityTreeAccessionsToMyList }
/>
/>
}
</div>
</PageContents>
) }
{ currentTab === 'view' && (treeDataLoading ? <Loading /> : treeData &&
<div className={ classes.treeWrapper }>
<ConfiguredTree
treeData={ [treeData] }
initialZoom={ 0.25 }
zoomable
// onClickHandler={ this.onNodeClick }
/>
</div>
{ currentTab === 'view' && (treeDataLoading ? <Loading /> : treeData && // todo: add error display
<TreeExplorer treeData={ treeData } />
) }
</PageLayout>
);
......
......@@ -81,9 +81,11 @@ function Layout({classes, children = null, sidebar = null, withFooter = false, c
<div className={ classes.children }>
{ children }
</div>
<div className={ classes.footer }>
{ withFooter && <Footer /> }
</div>
{ withFooter &&
<div className={ classes.footer }>
<Footer />
</div>
}
</div>
) }
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment