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 Component Icons + Node Appearance #339

Merged
merged 13 commits into from
Jul 11, 2024
Merged
22 changes: 11 additions & 11 deletions src/commands/NodeActionCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export function addNodeActionCommands(
nodeName = args['nodeName'] ?? node?.name;
nodeLineNo = args['nodeLineNo'] ?? node?.extras.lineNo;

if (nodeName.startsWith('Literal') || nodeName.startsWith('Argument')) {
if (nodeName.startsWith('Literal ') || nodeName.startsWith('Argument ')) {
showDialog({
title: `${node.name} don't have its own script`,
buttons: [Dialog.warnButton({ label: 'OK' })]
Expand Down Expand Up @@ -203,7 +203,7 @@ export function addNodeActionCommands(
isEnabled: () => {
let isNodeSelected: boolean;
const node = getLastSelectedNode();
if (node.getOptions()["name"].startsWith("Literal")) {
if (node.getOptions()["name"].startsWith("Literal ")) {
isNodeSelected = true;
}
return isNodeSelected ?? false;
Expand Down Expand Up @@ -243,17 +243,17 @@ export function addNodeActionCommands(
for (let selected_node of selected_nodes) {

if (
selected_node.name.startsWith("Literal") ||
selected_node.name.startsWith("Argument") ||
selected_node.name.startsWith("Start")
selected_node.name.startsWith("Literal ") ||
selected_node.name.startsWith("Argument ") ||
selected_node.name == "Start"
) {
console.info(selected_node.name + " cannot be reloaded.");
continue;
}

let node;

if (selected_node.name.startsWith("Finish")) {
if (selected_node.name == "Finish") {
node = BaseComponentLibrary('Finish');
} else {
// For other nodes, fetch from AdvancedComponentLibrary
Expand Down Expand Up @@ -727,12 +727,12 @@ export function addNodeActionCommands(

if (widget) {
const selected_node = getLastSelectedNode();
const nodeType = selected_node.getOptions()["name"];
const nodeName = selected_node.getOptions()["name"];
let updatedNode = null;

if (nodeType.startsWith("Literal")) {
if (nodeName.startsWith("Literal ")) {
updatedNode = await editLiteral(widget, selected_node);
} else if (nodeType.startsWith("Argument")) {
} else if (nodeName.startsWith("Argument ")) {
updatedNode = await editArgument(widget, selected_node);
} else {
showDialog({
Expand Down Expand Up @@ -778,7 +778,7 @@ export function addNodeActionCommands(
}

async function editLiteral(widget: XircuitsPanel, selected_node: any): Promise<any> {
if (!selected_node.getOptions()["name"].startsWith("Literal")) {
if (!selected_node.getOptions()["name"].startsWith("Literal ")) {
showDialog({
title: 'Only Literal Node can be edited',
buttons: [Dialog.warnButton({ label: 'OK' })]
Expand Down Expand Up @@ -806,7 +806,7 @@ export function addNodeActionCommands(
}

async function editArgument(widget: XircuitsPanel, selected_node: any): Promise<any> {
if (!selected_node.getOptions()["name"].startsWith("Argument")) {
if (!selected_node.getOptions()["name"].startsWith("Argument ")) {
showDialog({
title: 'Only Argument Node can be edited',
buttons: [Dialog.warnButton({ label: 'OK' })]
Expand Down
4 changes: 2 additions & 2 deletions src/components/XircuitsBodyWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ export const BodyWidget: FC<BodyWidgetProps> = ({

for (let i = 0; i < nodesCount; i++) {
let nodeName = allNodes[i].getOptions()["name"];
if (nodeName.startsWith("Argument")) {
if (nodeName.startsWith("Argument ")) {
let regEx = /\(([^)]+)\)/;
let result = nodeName.match(regEx);
let nodeText = nodeName.split(": ");
Expand Down Expand Up @@ -1050,7 +1050,7 @@ export const BodyWidget: FC<BodyWidgetProps> = ({
let point = xircuitsApp.getDiagramEngine().getRelativeMousePoint(event);
node.setPosition(point);
xircuitsApp.getDiagramEngine().getModel().addNode(node);
if (node["name"].startsWith("Argument")) {
if (node["name"].startsWith("Argument ")) {
setInitialize(true);
}
setSaved(false);
Expand Down
16 changes: 11 additions & 5 deletions src/components/link/CustomLinkFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,25 @@ namespace S {
animation: none !important;
}
`;

export const G = styled.g`
g.hover & path {
stroke: rgb(192, 255, 0);
}
`;
}

function addHover(model: TriangleLinkModel | ParameterLinkModel){
return (() => {
document.querySelector(`div.port[data-nodeid='${model.getSourcePort().getNode().getID()}'][data-name='${model.getSourcePort().getName()}']>div`).classList.add("hover");
document.querySelector(`div.port[data-nodeid="${model.getTargetPort().getNode().getID()}"][data-name='${model.getTargetPort().getName()}']>div`).classList.add("hover");
document.querySelector(`div.port[data-nodeid='${model.getSourcePort().getNode().getID()}'][data-name='${model.getSourcePort().getName()}']>div>div`).classList.add("hover");
document.querySelector(`div.port[data-nodeid="${model.getTargetPort().getNode().getID()}"][data-name='${model.getTargetPort().getName()}']>div>div`).classList.add("hover");
});
}

function removeHover(model: TriangleLinkModel | ParameterLinkModel){
return () => {
document.querySelector(`div.port[data-nodeid='${model.getSourcePort().getNode().getID()}'][data-name='${model.getSourcePort().getName()}']>div`).classList.remove("hover");
document.querySelector(`div.port[data-nodeid="${model.getTargetPort().getNode().getID()}"][data-name='${model.getTargetPort().getName()}']>div`).classList.remove("hover");
document.querySelector(`div.port[data-nodeid='${model.getSourcePort().getNode().getID()}'][data-name='${model.getSourcePort().getName()}']>div>div`).classList.remove("hover");
document.querySelector(`div.port[data-nodeid="${model.getTargetPort().getNode().getID()}"][data-name='${model.getTargetPort().getName()}']>div>div`).classList.remove("hover");
}
}

Expand Down Expand Up @@ -131,7 +137,7 @@ class SelectOnClickLinkWidget extends DefaultLinkWidget {
}
}

return <g data-default-link-test={this.props.link.getOptions().testName}>{paths}</g>;
return <S.G data-default-link-test={this.props.link.getOptions().testName}>{paths}</S.G>;
}
}

Expand Down
117 changes: 93 additions & 24 deletions src/components/node/CustomNodeWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,27 @@ 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";
import ReactTooltip from 'react-tooltip';
import { marked } from 'marked';
import Color from 'colorjs.io';
import { commandIDs } from "../../commands/CommandIDs";
import { commandIDs } from '../../commands/CommandIDs';
import {
componentLibIcon,
branchComponentIcon,
workflowComponentIcon,
functionComponentIcon,
startFinishComponentIcon,
variableComponentIcon,
setVariableComponentIcon,
getVariableComponentIcon } from '../../ui-components/icons';
import circuitBoardSvg from '../../../style/icons/circuit-board-bg.svg';



var S;
(function (S) {

S.Node = styled.div<{ borderColor: string, background: string; selected: boolean; }>`
background-color: ${(p) => {
const color = new Color(p.background);
color.alpha = 0.75;
color.oklch.c *= 1.2;
return color.to('oklch').toString();
}};
box-shadow: 1px 1px 10px ${(p) => p.selected ? '3px rgb(0 192 255 / 0.5)' : '0px rgb(0 0 0 / 0.5)'};
border-radius: 5px;
font-family: sans-serif;
Expand All @@ -33,17 +40,43 @@ var S;
border: solid 1px ${(p) => (p.selected ? (p.borderColor == undefined ? 'rgb(0,192,255)' : p.borderColor) : 'black')};
`;

S.Title = styled.div`
background: rgba(0, 0, 0, 0.3);
S.Title = styled.div<{ background: string; }>`
background-image: ${(p) => {
const color = new Color(p.background);
color.alpha = 0.75;
color.oklch.c *= 1.2;
const color1 = color.to('oklch').toString()
color.oklch.c *= 1.2;
color.oklch.l /= 2;
const color2 = color.to('oklch').toString()
return `linear-gradient(${color1}, ${color2})`
}};
font-weight: 500;
letter-spacing: 0.025rem;
display: flex;
white-space: nowrap;
justify-items: center;
box-shadow: inset 0 -2px 4px 0 rgb(0 0 0 / 0.05);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
`;

S.TitleName = styled.div`
flex-grow: 1;
padding: 5px 5px;
padding: 5px 5px 5px 5px;
`;

S.IconContainer = styled.div`
padding: 5px 5px 5px 5px;
display: flex;
align-items: center;
justify-content: center;
width: 15px;
height: 15px;
svg {
width: 100%;
height: 100%;
}
`;

S.CommentContainer = styled.div<{ selected: boolean; }>`
Expand All @@ -67,7 +100,13 @@ var S;

S.Ports = styled.div`
display: flex;
background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.2));
background-image: linear-gradient(oklch(10% 0 0 / 0.7), oklch(10% 0 0 / 0.9));
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;

.workflow-node & {
background: linear-gradient(oklch(10% 0 0 / 0.7), oklch(10% 0 0 / 0.9)), url("data:image/svg+xml;base64,${btoa(circuitBoardSvg)}") no-repeat right 10px;
}
`;

S.PortsContainer = styled.div`
Expand All @@ -85,13 +124,8 @@ var S;
margin-right: 0px;
}
`;
S.WorkflowNode = styled(S.Node)`
outline: 2px solid rgba(255, 255, 255, 0.2);
outline-offset: 8px; // Space between the main node and the outline

${(p) => p.selected && `
outline: 2px solid rgba(0, 192, 255, 0.2); // blue
`}
S.WorkflowNode = styled(S.Node)`
`;
})(S || (S = {}));

Expand All @@ -102,6 +136,33 @@ export interface DefaultNodeProps {
shell: ILabShell;
}

const getNodeIcon = (type) => {
switch (type) {
case 'Start':
case 'startFinish':
return <startFinishComponentIcon.react />;
case 'workflow':
case 'xircuits_workflow':
return <workflowComponentIcon.react />;
case 'branch':
return <branchComponentIcon.react />;
case 'function':
return <functionComponentIcon.react />;
case 'context_set':
return <setVariableComponentIcon.react />;
case 'context_get':
return <getVariableComponentIcon.react />;
case 'variable':
return <variableComponentIcon.react />;
// component libraries were typed as 'debug' before v1.12.
case 'debug':
case 'library_component':
return <componentLibIcon.react />;
default:
return null;
}
};

const CommentNode = ({ node }) => {
const [commentInput, setCommentInput] = React.useState(node['extras']['commentInput']);

Expand Down Expand Up @@ -134,7 +195,7 @@ const CommentNode = ({ node }) => {
const ParameterNode = ({ node, engine, app }) => {
const handleEditParameter = () => {
const nodeName = node.getOptions()["name"];
if (!nodeName.startsWith("Literal") && !nodeName.startsWith("Argument")) {
if (!nodeName.startsWith("Literal ") && !nodeName.startsWith("Argument ")) {
return;
}
app.commands.execute(commandIDs.editNode);
Expand All @@ -148,7 +209,9 @@ const ParameterNode = ({ node, engine, app }) => {
background={node.getOptions().color}
onDoubleClick={handleEditParameter}
>
<S.Title>
<S.Title background={node.getOptions().color}
>
{/* <S.IconContainer>{getNodeIcon('parameter')}</S.IconContainer> */}
<S.TitleName>{node.getOptions().name}</S.TitleName>
</S.Title>
<S.Ports>
Expand All @@ -166,7 +229,9 @@ const StartFinishNode = ({ node, engine, handleDeletableNode }) => (
selected={node.isSelected()}
background={node.getOptions().color}
>
<S.Title>
<S.Title background={node.getOptions().color}
>
<S.IconContainer>{getNodeIcon('startFinish')}</S.IconContainer>
<S.TitleName>{node.getOptions().name}</S.TitleName>
<label data-no-drag>
<Toggle className='lock' checked={node.isLocked() ?? false} onChange={event => handleDeletableNode('nodeDeletable', event)} />
Expand All @@ -181,7 +246,6 @@ const StartFinishNode = ({ node, engine, handleDeletableNode }) => (

const WorkflowNode = ({ node, engine, handleDeletableNode }) => {
const elementRef = React.useRef<HTMLElement>(null);

return (
<div style={{ position: "relative" }}>
<S.WorkflowNode
Expand All @@ -191,8 +255,11 @@ const WorkflowNode = ({ node, engine, handleDeletableNode }) => {
data-default-node-name={node.getOptions().name}
selected={node.isSelected()}
background={node.getOptions().color}
className="workflow-node"
>
<S.Title>
<S.Title background={node.getOptions().color}
>
<S.IconContainer>{getNodeIcon('workflow')}</S.IconContainer>
<S.TitleName>{node.getOptions().name}</S.TitleName>
<label data-no-drag>
<Toggle className='lock' checked={node.isLocked() ?? false} onChange={event => handleDeletableNode('nodeDeletable', event)} />
Expand Down Expand Up @@ -254,7 +321,9 @@ const ComponentLibraryNode = ({ node, engine, shell, handleDeletableNode }) => {
selected={node.isSelected()}
background={node.getOptions().color}
>
<S.Title>
<S.Title background={node.getOptions().color}
>
<S.IconContainer>{getNodeIcon(node['extras']['type'])}</S.IconContainer>
<S.TitleName>{node.getOptions().name}</S.TitleName>
<label data-no-drag>
<Toggle className='lock' checked={node.isLocked() ?? false} onChange={event => handleDeletableNode('nodeDeletable', event)} />
Expand Down
Loading