Skip to content

Commit

Permalink
Merge pull request #151 from XpressAI/adry/component-docstring-tooltip
Browse files Browse the repository at this point in the history
✨Add Component's Description Tooltip
  • Loading branch information
mansouralawi authored Apr 28, 2022
2 parents e82639b + 5894a7b commit 1060be5
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 88 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
"react-sanity-pagination": "^2.0.2",
"react-switch": "^6.0.0",
"react-textarea-autosize": "^8.3.3",
"react-toggle": "^4.1.2"
"react-toggle": "^4.1.2",
"react-tooltip": "^4.2.21"
},
"devDependencies": {
"@babel/core": "^7.12.10",
Expand Down
8 changes: 5 additions & 3 deletions src/components/CustomNodeFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ import { CustomNodeModel } from './CustomNodeModel';
import {AbstractReactFactory, GenerateModelEvent, GenerateWidgetEvent} from '@projectstorm/react-canvas-core';
import { DiagramEngine } from '@projectstorm/react-diagrams-core';
import {CustomNodeWidget} from "./CustomNodeWidget";
import { JupyterFrontEnd } from '@jupyterlab/application';
import { ILabShell, JupyterFrontEnd } from '@jupyterlab/application';

export class CustomNodeFactory extends AbstractReactFactory<CustomNodeModel, DiagramEngine> {
app : JupyterFrontEnd
constructor(app) {
shell : ILabShell
constructor(app, shell) {
super('custom-node');
this.app = app;
this.shell = shell;
}

generateModel(initialConfig: GenerateModelEvent) {
return new CustomNodeModel();
}

generateReactWidget(event: GenerateWidgetEvent<any>): JSX.Element {
return <CustomNodeWidget engine={this.engine as DiagramEngine} node={event.model} app={this.app}/>;
return <CustomNodeWidget engine={this.engine as DiagramEngine} node={event.model} app={this.app} shell={this.shell}/>;
}
}
227 changes: 173 additions & 54 deletions src/components/CustomNodeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import ToolTip from 'react-portal-tooltip';
import { Pagination } from "krc-pagination";
import 'krc-pagination/styles.css';
import Toggle from 'react-toggle'
import { JupyterFrontEnd } from '@jupyterlab/application';
import { ILabShell, JupyterFrontEnd } from '@jupyterlab/application';
import { commandIDs } from './xircuitBodyWidget';
import { CustomPortLabel } from './port/CustomPortLabel';
import TextareaAutosize from 'react-textarea-autosize';
import { Dialog } from '@jupyterlab/apputils';
import { formDialogWidget } from '../dialog/formDialogwidget';
import { showFormDialog } from '../dialog/FormDialog';
import { CommentDialog } from '../dialog/CommentDialog';
import ReactTooltip from 'react-tooltip';

var S;
(function (S) {
Expand Down Expand Up @@ -53,6 +54,14 @@ var S;
font-size: 12px;
border: solid 2px ${(p) => p.selected ? 'rgb(0,192,255)':'black'};
padding: 5px;
`;

S.DescriptionName = styled.div<{ color:string }>`
color: ${(p) => p.color ?? 'rgb(0, 0, 0)'};
text-align: justify;
font-family: 'Roboto', sans-serif;
font-weight: 700;
font-size: 13px;
`;

S.Ports = styled.div`
Expand Down Expand Up @@ -83,6 +92,7 @@ export interface DefaultNodeProps {
node: DefaultNodeModel;
engine: DiagramEngine;
app: JupyterFrontEnd;
shell : ILabShell;
}

/**
Expand All @@ -100,6 +110,7 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
isTooltipActive: false,
nodeDeletable: false,
commentInput: this.props.node['extras']['commentInput'],
showDescription: false,

imageGalleryItems:[
{
Expand Down Expand Up @@ -188,36 +199,169 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
this.handleOnChangeCanvas();
}

render() {
/**
* Show/Hide Component's Description Tooltip
*/
async handleDescription() {
await this.setState({ showDescription: !this.state.showDescription });
ReactTooltip.show(this.element as Element)
}

if(this.props.node.getOptions().extras["tip"]!=undefined&&this.props.node.getOptions().extras["tip"]!=""){
// Hide Error Tooltip
hideErrorTooltip(){
delete this.props.node.getOptions().extras["tip"];
this.props.node.getOptions().extras["borderColor"]="rgb(0,192,255)";
}

render() {
if (this.props.node.getOptions()["name"] !== 'Start' && this.props.node.getOptions()["name"] !== 'Finish') {
return (
<S.Node
onMouseEnter={this.showTooltip.bind(this)}
onMouseLeave={this.hideTooltip.bind(this)}
ref={(element) => { this.element = element }}
borderColor={this.props.node.getOptions().extras["borderColor"]}
data-default-node-name={this.props.node.getOptions().name}
selected={this.props.node.isSelected()}
background={this.props.node.getOptions().color}>
<ToolTip active={this.state.isTooltipActive} position="top" arrow="center" parent={this.element}>
<p>{this.props.node.getOptions().extras["tip"]}</p>
</ToolTip>
<S.Title>
<S.TitleName>{this.props.node.getOptions().name}</S.TitleName>
<label>
<Toggle
className='lock'
checked={this.props.node.isLocked()}
onChange={this.handleDeletableNode.bind(this, 'nodeDeletable')}
/>
</label>
</S.Title>
<S.Ports>
<S.PortsContainer>{_.map(this.props.node.getInPorts(), this.generatePort)}</S.PortsContainer>
<S.PortsContainer>{_.map(this.props.node.getOutPorts(), this.generatePort)}</S.PortsContainer>
</S.Ports>
</S.Node>
<>
<S.Node
onMouseEnter={this.showTooltip.bind(this)}
onMouseLeave={this.hideTooltip.bind(this)}
ref={(element) => { this.element = element }}
data-tip data-for={this.props.node.getOptions().id} // Data for tooltip
borderColor={this.props.node.getOptions().extras["borderColor"]}
data-default-node-name={this.props.node.getOptions().name}
selected={this.props.node.isSelected()}
background={this.props.node.getOptions().color}
onDoubleClick={this.handleEditLiteral.bind(this)}>
<S.Title>
<S.TitleName>{this.props.node.getOptions().name}</S.TitleName>
<label>
<Toggle
className='lock'
checked={this.props.node.isLocked()}
onChange={this.handleDeletableNode.bind(this, 'nodeDeletable')}
/>
<Toggle
className='description'
name='Description'
checked={this.state.showDescription}
onChange={this.handleDescription.bind(this)}
/>
</label>
</S.Title>
<S.Ports>
<S.PortsContainer>{_.map(this.props.node.getInPorts(), this.generatePort)}</S.PortsContainer>
<S.PortsContainer>{_.map(this.props.node.getOutPorts(), this.generatePort)}</S.PortsContainer>
</S.Ports>
</S.Node>
{/** Description Tooltip */}
{this.state.showDescription && <ReactTooltip
id={this.props.node.getOptions().id}
className='description-tooltip'
arrowColor='rgb(255, 255, 255)'
clickable
afterShow={() => { this.setState({ showDescription: true }) }}
afterHide={() => { this.setState({ showDescription: false }) }}
delayHide={60000}
delayUpdate={5000}
getContent={() =>
<div data-no-drag style={{ cursor: 'default' }}>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={() => { this.setState({ showDescription: false }); }}>
<span aria-hidden="true">&times;</span>
</button>
<S.DescriptionName color={this.props.node.getOptions().color}>{this.props.node.getOptions()["name"]}</S.DescriptionName>
<p className='description-title'>Description:</p>
<div className='description-container'>
<pre className='description-text'>{this.props.node['extras']['description'] ?? <i>No description provided</i>}</pre>
</div>
</div>}
overridePosition={(
{ left, top },
currentEvent, currentTarget, node, refNode) => {
const currentNode = this.props.node;
const nodeDimension = { x: currentNode.width, y: currentNode.height };
const nodePosition = { x: currentNode.getX(), y: currentNode.getY() };
let newPositionX = nodePosition.x;
let newPositionY = nodePosition.y;
let offset = 0;

if (!this.props.shell.leftCollapsed) {
// Some weird offset happened when left sidebar opened, need to add this
let leftSidebar = document.getElementById('jp-left-stack');
offset = leftSidebar.clientWidth + 2;
}

if (refNode == 'top') {
newPositionX = newPositionX - 208 + offset + (nodeDimension.x / 2);
newPositionY = newPositionY - 220;
}
else if (refNode == 'bottom') {
newPositionX = newPositionX - 208 + offset + (nodeDimension.x / 2);
newPositionY = newPositionY + 85 + nodeDimension.y;
}
else if (refNode == 'right') {
newPositionX = newPositionX + 40 + offset + nodeDimension.x;
newPositionY = newPositionY - 66 + (nodeDimension.y / 2);
}
else if (refNode == 'left') {
newPositionX = newPositionX - 450 + offset;
newPositionY = newPositionY - 66 + (nodeDimension.y / 2);
}
const tooltipPosition = this.props.engine.getRelativePoint(newPositionX, newPositionY);

left = tooltipPosition.x;
top = tooltipPosition.y;
return { top, left }
}}
/>}
{/** Error Tooltip */}
{(this.props.node.getOptions().extras["tip"] != undefined && this.props.node.getOptions().extras["tip"] != "") ?
<ReactTooltip
id={this.props.node.getOptions().id}
clickable
place='bottom'
className='error-tooltip'
arrowColor='rgba(255, 0, 0, .9)'
delayHide={100}
delayUpdate={50}
getContent={() =>
<div data-no-drag className='error-container'>
<p className='error-text'>{this.props.node.getOptions().extras["tip"]}</p>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={this.hideErrorTooltip.bind(this)}>
<span aria-hidden="true">&times;</span>
</button>
</div>
}
overridePosition={({ left, top }) => {
const currentNode = this.props.node;
const nodeDimension = { x: currentNode.width, y: currentNode.height };
const nodePosition = { x: currentNode.getX(), y: currentNode.getY() };
let newPositionX = nodePosition.x;
let newPositionY = nodePosition.y;
let offset = 0;

if (!this.props.shell.leftCollapsed) {
// Some weird offset happened when left sidebar opened, need to add this
let leftSidebar = document.getElementById('jp-left-stack');
offset = leftSidebar.clientWidth + 2;
}

newPositionX = newPositionX - 110 + offset + (nodeDimension.x / 2);
newPositionY = newPositionY + 90 + nodeDimension.y;

const tooltipPosition = this.props.engine.getRelativePoint(newPositionX, newPositionY);

left = tooltipPosition.x;
top = tooltipPosition.y;
return { top, left }
}}
/>
: null}
</>
);
}
else if(this.props.node.getOptions().extras["imageGalleryItems"] != undefined){
Expand Down Expand Up @@ -283,31 +427,6 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
</S.CommentContainer>
);
}
else if(this.props.node.getOptions()["name"] !== 'Start' && this.props.node.getOptions()["name"] !== 'Finish'){
return (
<S.Node
borderColor={this.props.node.getOptions().extras["borderColor"]}
data-default-node-name={this.props.node.getOptions().name}
selected={this.props.node.isSelected()}
background={this.props.node.getOptions().color}
onDoubleClick={this.handleEditLiteral.bind(this)}>
<S.Title>
<S.TitleName>{this.props.node.getOptions().name}</S.TitleName>
<label>
<Toggle
className='lock'
checked={this.props.node.isLocked()}
onChange={this.handleDeletableNode.bind(this, 'nodeDeletable')}
/>
</label>
</S.Title>
<S.Ports>
<S.PortsContainer>{_.map(this.props.node.getInPorts(), this.generatePort)}</S.PortsContainer>
<S.PortsContainer>{_.map(this.props.node.getOutPorts(), this.generatePort)}</S.PortsContainer>
</S.Ports>
</S.Node>
);
}
return (
<S.Node
borderColor={this.props.node.getOptions().extras["borderColor"]}
Expand Down
6 changes: 3 additions & 3 deletions src/components/XircuitsApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CustomNodeFactory } from "./CustomNodeFactory";
import { CustomNodeModel } from './CustomNodeModel';
import { ZoomCanvasAction } from '@projectstorm/react-canvas-core';
import { CustomActionEvent } from '../commands/CustomActionEvent';
import { JupyterFrontEnd } from '@jupyterlab/application';
import { ILabShell, JupyterFrontEnd } from '@jupyterlab/application';
import { CustomDiagramState } from './state/CustomDiagramState'
import { CustomLinkModel, TriangleLinkModel } from './link/CustomLinkModel';
import { CustomLinkFactory, TriangleLinkFactory } from './link/CustomLinkFactory';
Expand All @@ -14,11 +14,11 @@ export class XircuitsApplication {

protected diagramEngine: SRD.DiagramEngine;

constructor(app: JupyterFrontEnd) {
constructor(app: JupyterFrontEnd, shell: ILabShell) {

this.diagramEngine = SRD.default({ registerDefaultZoomCanvasAction: false, registerDefaultDeleteItemsAction: false });
this.activeModel = new SRD.DiagramModel();
this.diagramEngine.getNodeFactories().registerFactory(new CustomNodeFactory(app));
this.diagramEngine.getNodeFactories().registerFactory(new CustomNodeFactory(app, shell));
this.diagramEngine.getLinkFactories().registerFactory(new CustomLinkFactory());
this.diagramEngine.getLinkFactories().registerFactory(new TriangleLinkFactory());
this.diagramEngine.getActionEventBus().registerAction(new ZoomCanvasAction({ inverseZoom: true }))
Expand Down
5 changes: 3 additions & 2 deletions src/tray_library/AdvanceComponentLib.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) {
color: nodeData.color,
extras: {
"type": nodeData.type,
"path": nodeData.file_path
"path": nodeData.file_path,
"description": nodeData.docstring
}
});
node.addInPortEnhance('▶', 'in-0');
Expand All @@ -42,7 +43,7 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) {
"str": "string"
}

props.model["variables"].forEach(variable => {
nodeData["variables"].forEach(variable => {
let name = variable["name"];
let type = type_name_remappings[variable["type"]] || variable["type"];

Expand Down
6 changes: 4 additions & 2 deletions src/tray_library/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ export default function Sidebar(props: SidebarProps) {
type: componentVal.type,
name: componentVal.task,
color: componentVal.color,
path: componentVal.file_path
path: componentVal.file_path,
docstring: componentVal.docstring
}}
name={componentVal.task}
color={componentVal.color}
Expand Down Expand Up @@ -251,7 +252,8 @@ export default function Sidebar(props: SidebarProps) {
type: val.type,
name: val.task,
color: val.color,
path: val.file_path
path: val.file_path,
docstring: val.docstring
}}
name={val.task}
color={val.color}
Expand Down
Loading

0 comments on commit 1060be5

Please sign in to comment.