Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to display the description of the component parameters #200

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,8 @@ tsconfig.tsbuildinfo

# .py files compiled by Xircuits
examples/*.py

# pycharm files
/.idea/

xai_components/*/arguments
8 changes: 6 additions & 2 deletions src/components/CustomNodeModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ export class CustomNodeModel extends DefaultNodeModel {
this.extras=event.data.extras;
}

addOutPortEnhance(label: string, name: string, after: boolean = true, id?: string): CustomPortModel {
addOutPortEnhance(label: string, name: string, after: boolean = true, id?: string, description: string = ""): CustomPortModel {

//check if portID is passed, if not SR will generate a new port ID
const p = (id) ? new CustomPortModel({in: false, name: name, label: label, id:id}) :
new CustomPortModel({in: false, name: name, label: label});

p.description = description;

if (!after) {
this.portsOut.splice(0, 0, p);
Expand All @@ -54,12 +56,14 @@ export class CustomNodeModel extends DefaultNodeModel {
return this.addPort(p);
}

addInPortEnhance(label: string, name: string, after: boolean = true, id?: string): CustomPortModel {
addInPortEnhance(label: string, name: string, after: boolean = true, id?: string, description: string = ""): CustomPortModel {

//check if portID is passed, if not SR will generate a new port ID
const p = (id) ? new CustomPortModel({in: true, name: name, label: label, id:id}) :
new CustomPortModel({in: true, name: name, label: label});

p.description = description;

if (!after) {
this.portsOut.splice(0, 0, p);
}
Expand Down
99 changes: 82 additions & 17 deletions src/components/CustomNodeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,10 @@ export interface DefaultNodeProps {
*/
export class CustomNodeWidget extends React.Component<DefaultNodeProps> {

generatePort = (port) => {
return <CustomPortLabel engine={this.props.engine} port={port} key={port.getID()} node={this.props.node} />;
};
portsNo = this.props.node.getInPorts().length + this.props.node.getOutPorts().length;

tooltipDescriptionRef = React.createRef<HTMLDivElement>();

element:Object;
state = {

Expand All @@ -127,7 +128,63 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
original: 'https://picsum.photos/id/1019/1000/600/',
thumbnail: 'https://picsum.photos/id/1019/250/150/'
},
]
],
showParamDescriptionList: new Array(this.portsNo).fill(false),
paramName: ""
};

/**
* creates a particular function for each component so that it can set only it's state
* @param id
*/
setShowParamDescription = (id : number) => {
const _setShowParamDescription = (newShowDescription : boolean) => {
this.setState({
showParamDescriptionList: this.state.showParamDescriptionList.map((value, index) => (
id === index ? newShowDescription : false
)
),
showDescription: false
})
}
return _setShowParamDescription;
}

setDescriptionStr = (paramName: string) => {
const _setDescriptionStr = async (descriptionStr : string) => {
await this.setState({
descriptionStr : descriptionStr,
paramName: paramName
});
ReactTooltip.show(this.element as Element);
}
return _setDescriptionStr;
}

generatePort = (port, index) => {
const argumentDescriptions = this.props.node['extras']['argumentDescriptions'];

// remove the ☆ from the beginning of the label
const name = port.getOptions().label[0] === "★" ? port.getOptions().label.slice(1) : port.getOptions().label;

const description = argumentDescriptions && (name in argumentDescriptions) ? argumentDescriptions[name] : "";

const isOutPort = port.getOptions().name.includes('parameter-out');

index = isOutPort ? index + this.props.node.getInPorts().length: index;

return (
<CustomPortLabel
engine={this.props.engine}
port={port}
key={port.getID()}
node={this.props.node}
showDescription={this.state.showParamDescriptionList[index]}
setShowDescription={this.setShowParamDescription(index)}
setDescriptionStr = {this.setDescriptionStr}
description={description}
/>
);
};

showTooltip() {
Expand Down Expand Up @@ -206,8 +263,12 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
* Show/Hide Component's Description Tooltip
*/
async handleDescription() {
await this.setState({ showDescription: !this.state.showDescription });
this.getDescriptionStr();
await this.setState({
showDescription: !this.state.showDescription,
showParamDescriptionList: new Array(this.portsNo).fill(false),
paramName: ""
});
await this.getDescriptionStr();
ReactTooltip.show(this.element as Element);
}

Expand All @@ -234,7 +295,7 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
delete this.props.node.getOptions().extras["tip"];
this.props.node.getOptions().extras["borderColor"]="rgb(0,192,255)";
}

render() {
if (this.props.node['extras']['type'] == 'comment') {
return (
Expand Down Expand Up @@ -301,39 +362,43 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {
</S.Ports>
</S.Node>
{/** Description Tooltip */}
{this.state.showDescription && <ReactTooltip
{(this.state.showDescription || this.state.showParamDescriptionList.reduce((prev, cur) => prev || cur, false)) && <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}
delayUpdate={0}
getContent={() =>
<div data-no-drag style={{ cursor: 'default' }}>
<div data-no-drag style={{ cursor: 'default' }} ref={this.tooltipDescriptionRef}>
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={() => { this.setState({ showDescription: false }); }}>
onClick={() => { this.setState({
showDescription: false,
showParamDescriptionList: new Array(this.portsNo).fill(false)
}); }}
>
<span aria-hidden="true">&times;</span>
</button>
<S.DescriptionName color={this.props.node.getOptions().color}>{this.props.node.getOptions()["name"]}</S.DescriptionName>
<S.DescriptionName color={this.props.node.getOptions().color}>{this.props.node.getOptions()["name"] + " " + this.state.paramName}</S.DescriptionName>
<p className='description-title'>Description:</p>
<div
<div
onWheel={(e) => e.stopPropagation()}
className='description-container'>
<div className='markdown-body' dangerouslySetInnerHTML={this.renderText(this.state.descriptionStr)} />
<div className='markdown-body' dangerouslySetInnerHTML = {{__html : this.state.descriptionStr}}/>
</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;
Expand All @@ -346,7 +411,7 @@ export class CustomNodeWidget extends React.Component<DefaultNodeProps> {

if (refNode == 'top') {
newPositionX = newPositionX - 208 + offset + (nodeDimension.x / 2);
newPositionY = newPositionY - 220;
newPositionY = newPositionY + 66 - this.tooltipDescriptionRef.current.clientHeight;
}
else if (refNode == 'bottom') {
newPositionX = newPositionX - 208 + offset + (nodeDimension.x / 2);
Expand Down
16 changes: 15 additions & 1 deletion src/components/port/CustomPortLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import * as React from 'react';
import { DiagramEngine, PortWidget } from '@projectstorm/react-diagrams-core';
import { DefaultNodeModel, DefaultPortModel } from "@projectstorm/react-diagrams";
import styled from '@emotion/styled';
import WithToggle from "./WithToggle";


export interface CustomPortLabelProps {
port: DefaultPortModel;
engine: DiagramEngine;
node: DefaultNodeModel;
showDescription: boolean;
setShowDescription: any;
setDescriptionStr: (string: any) => any;
description : string;
}

namespace S {
Expand Down Expand Up @@ -123,7 +129,15 @@ export class CustomPortLabel extends React.Component<CustomPortLabelProps> {

const label = (
<S.Label>
{this.props.port.getOptions().label}
<WithToggle
renderToggleBeforeChildren={!this.props.port.getOptions().in}
showDescription={this.props.showDescription}
setShowDescription={this.props.setShowDescription}
description={this.props.description}
setDescriptionStr={this.props.setDescriptionStr(this.props.port.getOptions().label)}
>
{this.props.port.getOptions().label}
</WithToggle>
</S.Label>);

return (
Expand Down
11 changes: 11 additions & 0 deletions src/components/port/CustomPortModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import {PortModel} from "@projectstorm/react-diagrams-core";
*/
export class CustomPortModel extends DefaultPortModel {

private _description : string;

get description(): string {
return this._description;
}

set description(value: string) {
this._description = value;
}



canLinkToPort(port: PortModel): boolean {
if (port instanceof DefaultPortModel) {
Expand Down
50 changes: 50 additions & 0 deletions src/components/port/WithToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from "react";
import Toggle from 'react-toggle'
import {useCallback, useRef} from "react";
import {WithToggleProps} from "./types";


export default function WithToggle(props: WithToggleProps){
const ref = useRef(null);

const changeHandler = useCallback(async () => {
await props.setShowDescription(!props.showDescription)
await props.setDescriptionStr(props.description)
}, [props.description, props.showDescription])

return (
<div ref ={ref} className="alignToggle">
{props.renderToggleBeforeChildren ?
<>
{
props.description &&
<Toggle
className='description'
name='Description'
checked={props.showDescription}
onChange={changeHandler}
/>
}
<span style={{display: "inline-block", paddingLeft: "0.3rem"}}>
{props.children}
</span>
</>
:
<>
<span style={{display: "inline-block", paddingRight: "0.3rem"}}>
{props.children}
</span>
{
props.description &&
<Toggle
className='description'
name='Description'
checked={props.showDescription}
onChange={changeHandler}
/>
}
</>
}
</div>
)
}
14 changes: 14 additions & 0 deletions src/components/port/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {ReactElement} from "react";

export type WithToggleProps = {
renderToggleBeforeChildren : boolean;
children: ReactElement[] | ReactElement | string;
showDescription: boolean;
setShowDescription: any;
setDescriptionStr: (param: string) => void;
description: string;
}

export type WithToggleState = {
showDescription : boolean
}
20 changes: 13 additions & 7 deletions src/tray_library/AdvanceComponentLib.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) {
extras: {
"type": nodeData.type,
"path": nodeData.file_path,
"description": nodeData.docstring,
"description": nodeData["json_description"]["description"] || nodeData.docstring,
"argumentDescriptions" : nodeData["json_description"]["arguments"],
"lineNo": nodeData.lineno
}
});
Expand All @@ -44,19 +45,24 @@ export function AdvancedComponentLibrary(props: AdvancedComponentLibraryProps) {
"str": "string"
}

nodeData["variables"].forEach(variable => {
let name = variable["name"];
let type = type_name_remappings[variable["type"]] || variable["type"];
const argumentDescriptions = nodeData["json_description"]["arguments"];


nodeData["variables"].forEach((variable, _) => {
const name = variable["name"];
const type = type_name_remappings[variable["type"]] || variable["type"];

const description = argumentDescriptions ? argumentDescriptions[name] || "" : "";

switch (variable["kind"]) {
case "InCompArg":
node.addInPortEnhance(`★${name}`, `parameter-${type}-${name}`);
node.addInPortEnhance(`★${name}`, `parameter-${type}-${name}`, true, null, description);
break;
case "InArg":
node.addInPortEnhance(name, `parameter-${type}-${name}`);
node.addInPortEnhance(name, `parameter-${type}-${name}`, true, null, description);
break;
case "OutArg":
node.addOutPortEnhance(name, `parameter-out-${type}-${name}`);
node.addOutPortEnhance(name, `parameter-out-${type}-${name}`, true, null, description);
break;
case "BaseComponent":
node.addOutPortEnhance(`${name} ▶`, `out-flow-${name}`);
Expand Down
5 changes: 3 additions & 2 deletions style/ComponentInfo.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
}

.description-container{
width: 450px;
height: 250px;
width: 600px;
max-height: 250px;
overflow: auto;
display: inline-block;
}

.description-title{
Expand Down
13 changes: 13 additions & 0 deletions style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,17 @@ input[type=number]::-webkit-inner-spin-button {
/* Fix arrow position of HTMLSelect tag on run dialog */
.jp-Dialog-content .f1ozlkqi {
position: relative;
}

.alignToggle {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}

.alignToggle span {
display: inline-block;
padding: 0;
margin: 0;
}
Loading